
URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox ChangeLog: 2009-03-06 Fabien Freling <fabien.freling@lrde.epita.fr> Add watershed implementation for IRM images. * fabien/bin/Makefile: Update. * fabien/bin/dicom2dump.cc: Update. * fabien/bin/dicom_mask.cc: Update. * fabien/igr/Makefile: Update. * fabien/igr/dumps: Remove. * fabien/igr/watershed.cc: Watershed implementation. --- bin/Makefile | 3 bin/dicom2dump.cc | 19 --- bin/dicom_mask.cc | 26 ++++ igr/Makefile | 9 - igr/watershed.cc | 320 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 353 insertions(+), 24 deletions(-) Index: trunk/milena/sandbox/fabien/igr/watershed.cc =================================================================== --- trunk/milena/sandbox/fabien/igr/watershed.cc (revision 0) +++ trunk/milena/sandbox/fabien/igr/watershed.cc (revision 3496) @@ -0,0 +1,320 @@ +#include <iostream> +#include <mln/core/image/image2d.hh> + +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/alias/window2d.hh> +#include <mln/core/image/image_if.hh> + +#include <mln/io/ppm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/load.hh> +#include <mln/io/dicom/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/dump/save.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> +#include <mln/value/int_u12.hh> +#include <mln/value/label_16.hh> + +#include <mln/level/transform.hh> +#include <mln/level/stretch.hh> + +#include <mln/labeling/mean_values.hh> + +#include <mln/convert/to_fun.hh> + +#include <mln/make/graph.hh> + +#include <mln/morpho/gradient.hh> +#include <mln/morpho/closing/area.hh> +#include <mln/morpho/meyer_wst.hh> + +#include <mln/fun/l2l/wrap.hh> + +#include <mln/core/var.hh> +#include <mln/morpho/elementary/dilation.hh> + +#include <mln/core/routine/extend.hh> + +#include <mln/util/graph.hh> + +#include <mln/essential/2d.hh> +#include <mln/core/alias/vec3d.hh> +#include <mln/debug/draw_graph.hh> +#include <mln/util/graph.hh> +#include <mln/accu/center.hh> +#include <mln/io/dump/all.hh> +#include <mln/value/label_8.hh> +#include <mln/value/rgb16.hh> +#include <mln/accu/compute.hh> +#include <mln/core/alias/dpoint2d.hh> +#include <mln/draw/box.hh> +#include <mln/level/stretch.hh> +#include <mln/fun/v2v/id.hh> +#include <mln/core/image/line_graph_elt_neighborhood.hh> +#include <mln/morpho/elementary/dilation.hh> +#include <mln/labeling/mean_values.hh> +#include <mln/extension/adjust_fill.hh> +#include <mln/extract/all.hh> +#include <mln/make/region_adjacency_graph.hh> + + + +// Given a color image and a wshed image, computes the component graph. +// Vertex values are computed thanks to a RGB image. + +namespace mln +{ + + template <typename V> + value::int_u8 dist(const V& c1, const V& c2) + { + unsigned d = math::diff_abs(c1.red(), c2.red()); + unsigned d_; + d_ = math::diff_abs(c1.green(), c2.green()); + + if (d_ > d) + d = d_; + + d_ = math::diff_abs(c1.blue(), c2.blue()); + + if (d_ > d) + d = d_; + return d; + } + + + // ima_v, image on graph vertices; value = mean color per vertex (watershed basin) + template <typename I> + inline + pw::image<fun::i2v::array<value::int_u8>, + p_edges<util::graph, fun::i2v::array<mln_site(I)> > > + make_edge_graph_image(const I& ima_v, const util::graph& g) + { + // edge sites. + typedef fun::i2v::array<mln_site(I)> edge_site_t; + edge_site_t edge_site(g.e_nmax(), literal::origin); + typedef p_edges<util::graph, edge_site_t > pe_t; + pe_t pe(g, edge_site); + + // edge values + typedef fun::i2v::array<value::int_u8> edge_values_t; + edge_values_t edge_values(g.e_nmax()); + + // image on graph edges + typedef pw::image<edge_values_t, pe_t> ima_e_t; + ima_e_t ima_e = (edge_values | pe); + + mln_piter(ima_e_t) e(ima_e.domain()); + for_all(e) // in ima_e + ima_e(e) = math::diff_abs(ima_v.function()(e.element().v1()), ima_v.function()(e.element().v2())); + + return ima_e; + } + + + template <typename I, typename J> + inline + util::array<mln_value(I)> + mean_color_values(const I& input, const J& w, mln_value(J) nbasins) + { + // Cf. sandbox/theo/color/segment_rgb_pixels.cc + + util::array<vec3d_f> m_3f = labeling::compute(accu::mean<mln_value(I)>(), + input, // input color image + w, // watershed labeling + nbasins); + m_3f[0] = literal::zero; + + util::array<mln_value(I)> m; + convert::from_to(m_3f, m); + m[0] = literal::yellow; + + io::ppm::save(level::transform(w, + convert::to< fun::i2v::array<mln_value(I)> >(m)), + "wst_rag_wshd_color.ppm"); + + return m; + } + + + template <typename I, typename J> + pw::image<fun::i2v::array<mln_value(I)>, p_vertices<util::graph, fun::i2v::array<mln_site(I)> > > + make_vertex_graph_image(const util::graph& g, const I&input, const J& w, const mln_value(J)& nbasins) + { + typedef util::array<mln_site(I)> vertex_sites_t; + vertex_sites_t site_values; + convert::from_to(labeling::compute(accu::center<mln_site(I)>(), w, nbasins), + site_values); + + typedef fun::i2v::array<mln_site(J)> f_sites_t; + f_sites_t sites; + convert::from_to(site_values, sites); + + // p_vertices + typedef p_vertices<util::graph, f_sites_t> S; + S pv(g, sites); + + + typedef fun::i2v::array<mln_value(I)> vertex_values_t; + vertex_values_t vertex_values; + // convert::from_to(mean_color_values(input, w, nbasins), vertex_values); + + mln_VAR(ima_v, (vertex_values | pv)); + + return ima_v; + } + + + template <typename I, typename V> + struct edge_to_color : Function_p2v< edge_to_color<I,V> > + { + typedef V result; + + edge_to_color(const I& ima) + : ima_(ima) + { + } + + V + operator()(const unsigned& e) const + { + return convert::to<V>(ima_.function()(e)); + } + + const I& ima_; + }; + + template <typename V> + inline + unsigned + find_root(util::array<V>& parent, const unsigned& x) + { + if (parent[x] == x) + return x; + else + return parent[x] = find_root(parent, parent[x]); + } + + + template <typename I, typename V, typename E> + inline + image2d<mln_value(I)> + make_debug_graph_image(const I& input, + const V& ima_v, const E& ima_e, + unsigned box_size, const value::rgb8& bg) + { + image2d<mln_value(I)> ima; + initialize(ima, input); + + data::fill(ima, bg); + debug::draw_graph(ima, ima_v.domain(), + pw::cst(mln_value(I)(literal::green)), + edge_to_color<E, mln_value(I)>(ima_e)); + + dpoint2d tl(-box_size,-box_size); + dpoint2d br(box_size,box_size); + mln_piter(V) p(ima_v.domain()); + for_all(p) + { + box2d b(p + tl, p + br); + b.crop_wrt(ima.domain()); + data::fill((ima | b).rw(), convert::to<mln_value(I)>(ima_v(p))); + } + + return ima; + } + +} + + +/////////////////// +// // +// Main Function // +// // +/////////////////// + + +int main(int argc, char *argv[]) +{ + using namespace mln; + using value::int_u8; + using value::int_u12; + using value::rgb8; + using value::label_16; + + if (argc < 4) + { + std::cout << "Usage: " << argv[0] << " <ima.dcm> <closure_lambda> <box_size>" + << std::endl; + return 1; + } + + unsigned box_size = atoi(argv[3]); + + image2d<int_u12> dcm; + io::dicom::load(dcm, argv[1]); + + image2d<int_u12> grad = morpho::gradient(dcm, win_c4p()); + image2d<int_u12> clo = morpho::closing::area(grad, c4(), atoi(argv[2])); + + label_16 nbasins; + image2d<label_16> wshed = morpho::meyer_wst(clo, c4(), nbasins); + + mln_VAR(vol2_, morpho::elementary::dilation(extend(wshed | (pw::value(wshed) == 0u), wshed), c8())); + + data::fill((wshed | (pw::value(wshed) == 0u)).rw(), vol2_); + + /// Build graph + util::graph g = make::graph(wshed, c4(), nbasins); + // Build graph images and compute distance values with a RGB image. + mln_VAR(ima_v, make_vertex_graph_image(g, dcm, wshed, nbasins)); + mln_VAR(ima_e, make_edge_graph_image(ima_v, g)); + + /// Try to merge vertices. + mln_piter_(ima_e_t) e(ima_e.domain()); + util::array<label_16> parent(g.v_nmax()); + for (unsigned i = 0; i < parent.nelements(); ++i) + parent[i] = i; + + for_all(e) + { + unsigned v1 = e.element().v1(); + unsigned v2 = e.element().v2(); + if (ima_e(e) <= (unsigned)(atoi(argv[5])) + && find_root(parent, v1) != find_root(parent, v2)) + parent[find_root(parent, v1)] = find_root(parent, v2); + } + + fun::i2v::array<label_16> f(parent.nelements()); + std::vector<unsigned> new_label(parent.nelements(), 0); + unsigned nbasins2 = 0; + for (unsigned i = 0; i < parent.nelements(); ++i) + { + unsigned p = find_root(parent, i); + mln_assertion(parent[p] == find_root(parent, i)); + if (new_label[p] == 0) + new_label[p] = nbasins2++; + f(i) = new_label[p]; + } + mln_invariant(f(0) == 0u); + --nbasins2; // nbasins2 does not count the basin with label 0. + image2d<label_16> wsd2 = level::transform(wshed, f); + + + /// Reconstruct a graph from the simplified image. + util::graph g2 = make::graph(wsd2, c4(), nbasins2); + + // Compute distance values with a RGB image. + mln_VAR(ima_v2, make_vertex_graph_image(g2, dcm, wsd2, nbasins2)); + mln_VAR(ima_e2, make_edge_graph_image(ima_v2, g2)); + + mln_VAR(wsd2_, morpho::elementary::dilation(extend(wsd2 | (pw::value(wsd2) == 0u), wsd2), c8())); + + data::fill((wsd2 | (pw::value(wsd2) == 0u)).rw(), wsd2_); + + //io::ppm::save(labeling::mean_values(dcm, wsd2, nbasins2), "wst_rag_mean_colors.ppm"); + //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::white), "wst_rag_graph_image2_white.ppm"); + //io::ppm::save(make_debug_graph_image(dcm, ima_v2, ima_e2, box_size, literal::black), "wst_rag_graph_image2_black.ppm"); +} Index: trunk/milena/sandbox/fabien/igr/Makefile =================================================================== --- trunk/milena/sandbox/fabien/igr/Makefile (revision 3495) +++ trunk/milena/sandbox/fabien/igr/Makefile (revision 3496) @@ -8,7 +8,7 @@ -framework CoreFoundation CXXFLAGS = -DNDEBUG -O1 -all: 2d 3d +all: 2d 3d wsd 2d: seg_vol_irm.hh seg2d.cc g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg2d.cc -o seg2d @@ -16,11 +16,8 @@ 3d: seg_vol_irm.hh seg3d.cc g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} seg3d.cc -o seg3d -grad: grad_clo_and_wshd.cc - g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o grad_clo - -wst: wst_rag.cc - g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wst_rag +wsd: watershed.cc + g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o wsd clean: rm -rf *.dump *.p?m *.plot *.log *.csv Index: trunk/milena/sandbox/fabien/bin/dicom2dump.cc =================================================================== --- trunk/milena/sandbox/fabien/bin/dicom2dump.cc (revision 3495) +++ trunk/milena/sandbox/fabien/bin/dicom2dump.cc (revision 3496) @@ -2,7 +2,6 @@ #include <mln/core/image/image2d.hh> #include <mln/core/image/image3d.hh> -#include <mln/value/int_u8.hh> #include <mln/value/int_u12.hh> #include <mln/io/dicom/load.hh> #include <mln/io/dump/save.hh> @@ -17,31 +16,15 @@ } -/*int main(int argc, char* argv[]) -{ - using namespace mln; - using value::int_u12; - - if (argc != 3) - return usage(argv); - - image3d<int_u12> ima; - io::dicom::load(ima, argv[1]); - io::dump::save(ima, argv[2]); - - return 0; -}*/ - int main(int argc, char* argv[]) { using namespace mln; - using value::int_u8; using value::int_u12; if (argc != 3) return usage(argv); - image2d<int_u8> ima; + image3d<int_u12> ima; io::dicom::load(ima, argv[1]); io::dump::save(ima, argv[2]); Index: trunk/milena/sandbox/fabien/bin/dicom_mask.cc =================================================================== --- trunk/milena/sandbox/fabien/bin/dicom_mask.cc (revision 3495) +++ trunk/milena/sandbox/fabien/bin/dicom_mask.cc (revision 3496) @@ -1,6 +1,9 @@ #include <mln/core/concept/image.hh> #include <mln/core/image/image2d.hh> +#include <mln/geom/ncols.hh> +#include <mln/geom/nrows.hh> + #include <mln/value/int_u8.hh> #include <mln/io/dicom/load.hh> #include <mln/io/pbm/save.hh> @@ -33,6 +36,29 @@ image2d<int_u8> input; io::dicom::load(input, argv[1]); + util::array<unsigned> xproj(geom::nrows(input), 0); + util::array<unsigned> yproj(geom::ncols(input), 0); + + mln_piter_(image2d<int_u8>) p(input.domain()); + for_all(p) + { + xproj[p.row()] += input(p); + yproj[p.col()] += input(p); + } + + // Plot files + std::ofstream fout_x("x.plot"); + for (unsigned int i = 0; i < xproj.nelements(); ++i) + { + fout_x << i << " " << xproj[i] << std::endl; + } + + std::ofstream fout_y("y.plot"); + for (unsigned int i = 0; i < yproj.nelements(); ++i) + { + fout_y << i << " " << yproj[i] << std::endl; + } + image2d<bool> ima = level::transform(input, fun::v2b::threshold<int_u8>(1)); io::pbm::save(ima, argv[2]); } Index: trunk/milena/sandbox/fabien/bin/Makefile =================================================================== --- trunk/milena/sandbox/fabien/bin/Makefile (revision 3495) +++ trunk/milena/sandbox/fabien/bin/Makefile (revision 3496) @@ -21,3 +21,6 @@ dump: dicom2dump.cc g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o dicom2dump + +pgm: dicom2pgm.cc + g++ -I../../../ ${DICOM_INC} ${DICOM_LIB} ${CXXFLAGS} $^ -o dicom2pgm