LRE
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
July
June
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
March
February
January
2007
December
November
October
September
August
July
June
May
April
March
February
January
2006
December
November
October
September
August
July
June
May
April
March
February
January
2005
December
November
October
September
August
July
June
May
April
March
February
January
2004
December
November
October
September
August
July
June
May
April
March
List overview
Download
Olena-patches
----- 2025 -----
July 2025
June 2025
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
----- 2005 -----
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
----- 2004 -----
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
olena-patches@lrde.epita.fr
9625 discussions
Start a n
N
ew thread
last-svn-commit-211-gb57d26e Fix last details in the image processing chain.
by green
* green/tools/annotating/histo/histo.cc: Manage new inputs/outputs. * green/tools/annotating/opening/opening.cc: Manage new inputs/outputs. * green/tools/annotating/iz/Makefile.am: New Makefile. * green/tools/annotating/iz/iz.cc: New file. * green/tools/annotating/regmax/regmax.cc: Manage new inputs/outputs. --- milena/sandbox/ChangeLog | 10 + .../sandbox/green/tools/annotating/histo/histo.cc | 84 +++-- .../tools/annotating/{histo => iz}/Makefile.am | 0 milena/sandbox/green/tools/annotating/iz/iz.cc | 373 ++++++++++++++++++++ .../green/tools/annotating/opening/opening.cc | 76 +++-- .../green/tools/annotating/regmax/regmax.cc | 253 ++++++++++++-- 6 files changed, 714 insertions(+), 82 deletions(-) copy milena/sandbox/green/tools/annotating/{histo => iz}/Makefile.am (100%) create mode 100644 milena/sandbox/green/tools/annotating/iz/iz.cc diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index f56ea7a..861e635 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -75,6 +75,16 @@ * bin/pgm_to_pbm.cc, * bin/ppm_negate.cc: New. +2010-02-10 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Fix last details in the image processing chain. + + * green/tools/annotating/histo/histo.cc: Manage new inputs/outputs. + * green/tools/annotating/opening/opening.cc: Manage new inputs/outputs. + * green/tools/annotating/iz/Makefile.am: New Makefile. + * green/tools/annotating/iz/iz.cc: New file. + * green/tools/annotating/regmax/regmax.cc: Manage new inputs/outputs. + 2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Extend the histogram visualization tools for new projection concept. diff --git a/milena/sandbox/green/tools/annotating/histo/histo.cc b/milena/sandbox/green/tools/annotating/histo/histo.cc index ab0b8af..8037e1a 100644 --- a/milena/sandbox/green/tools/annotating/histo/histo.cc +++ b/milena/sandbox/green/tools/annotating/histo/histo.cc @@ -16,14 +16,14 @@ #include <mln/fun/v2v/rgb8_to_rgbn.hh> -#include <mln/io/dump/save.hh> #include <mln/io/pbm/load.hh> -#include <mln/io/pbm/save.hh> -#include <mln/io/pgm/load.hh> -#include <mln/io/pgm/save.hh> #include <mln/io/ppm/load.hh> +#include <mln/io/dump/save.hh> +#include <mln/io/pgm/save.hh> #include <mln/io/ppm/save.hh> +#include <mln/literal/colors.hh> + #include <mln/opt/at.hh> #include <mln/pw/value.hh> @@ -33,15 +33,19 @@ template <unsigned n> -void mk_histo(const std::string& input, - const std::string& output, - const std::string& histo, - const std::string& mask) +void mk_histo(const std::string& input, // in + const std::string& quant, // in + const std::string& histo, // out + const std::string& proj1, // out + const std::string& proj2, // out + const std::string& mask) // [in] { typedef mln::value::int_u8 t_int_u8; + typedef mln::value::int_u<n> t_int_un; typedef mln::value::rgb8 t_rgb8; typedef mln::value::rgb<n> t_rgbn; typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_int_un> t_image2d_int_un; typedef mln::image2d<t_rgb8> t_image2d_rgb8; typedef mln::image2d<t_rgbn> t_image2d_rgbn; typedef mln::image2d<bool> t_image2d_bool; @@ -54,7 +58,9 @@ void mk_histo(const std::string& input, t_image2d_rgbn i1_input; // input rgbn t_image2d_bool m0_input; // mask input t_histo3d h1_input; // histo input - t_image2d_int_u8 p1_histo; // histo proj + t_image2d_int_u8 p1_histo1;// histo proj1 + t_image2d_rgbn p1_histo2;// histo proj2 + t_rgbn red(mln::literal::red); mln::io::ppm::load(i0_input, input.c_str()); i1_input = mln::data::transform(i0_input, t_rgb8_to_rgbn()); @@ -72,9 +78,13 @@ void mk_histo(const std::string& input, // END OF IMAGE PROCESSING CHAIN // BEGIN DUMPING - p1_histo = mln::display::display_histo3d_unsigned(h1_input); + p1_histo1 = mln::display::display_histo3d_unsigned(h1_input); + p1_histo2 = mln::display::display3_histo3d_unsigned(h1_input, red); + + mln::io::ppm::save(i1_input, quant.c_str()); mln::io::dump::save(h1_input, histo.c_str()); - mln::io::pgm::save(p1_histo, output.c_str()); + mln::io::pgm::save(p1_histo1, proj1.c_str()); + mln::io::ppm::save(p1_histo2, proj2.c_str()); // END DUMPING } @@ -82,35 +92,47 @@ void mk_histo(const std::string& input, void usage() { std::cout << std::endl; - std::cout << "histo input.ppm q out.ppm histo.dump [msk.pbm]" << std::endl; - std::cout << "where" << std::endl; - std::cout << "input.ppm is the 8 bits color ppm image" << std::endl; - std::cout << "q is the degree of quanification {2,3,4,5,6,7,8}" << std::endl; - std::cout << "out.pgm is the r/g projection of the histogram" << std::endl; - std::cout << "out.dump is the quantified color histogram" << std::endl; - std::cout << "msk.pbm is the mask which select the pixels" << std::endl; + std::cout << "histo input.ppm q quant.ppm histo.dump proj.pgm" + << " proj.ppm [msk.pbm]" << std::endl; + std::cout << std::endl; + std::cout << "where :" << std::endl; + std::cout << "* [ in] input.ppm is the 8 bits color ppm image" << std::endl; + std::cout << "* [ in] q is the degree of quantification" + << " {2,3,4,5,6,7,8}" << std::endl; + std::cout << "* [out] quant.ppm is the q bits quantified input" + << " image" << std::endl; + std::cout << "* [out] histo.dump is the quantified color" + << " histogram" << std::endl; + std::cout << "* [out] proj.pgm is the r/g projection of the" + << " histogram (summing along the blue axe)" << std::endl; + std::cout << "* [out] proj.ppm is the r/g projection of the" + << " histogram with maxima plot on" << std::endl; + std::cout << "* [ in] msk.pbm is the mask which selects the" + << " pixels" << std::endl; std::cout << std::endl; } int main(int argc, char* args[]) { - if (5 == argc || 6 == argc) + if (7 == argc || 8 == argc) { - const std::string input(args[1]); - const char q = args[2][0]; - const std::string output(args[3]); - const std::string histo(args[4]); - const std::string mask(6 == argc? args[5] : ""); + const std::string input(args[1]); // in + const char q = args[2][0]; // in + const std::string quant(args[3]); // out + const std::string histo(args[4]); // out + const std::string proj1(args[5]); // out + const std::string proj2(args[6]); // out + const std::string mask(8 == argc? args[7] : ""); // [in] switch(q) { - case '2': mk_histo<2>(input, output, histo, mask); break; - case '3': mk_histo<3>(input, output, histo, mask); break; - case '4': mk_histo<4>(input, output, histo, mask); break; - case '5': mk_histo<5>(input, output, histo, mask); break; - case '6': mk_histo<6>(input, output, histo, mask); break; - case '7': mk_histo<7>(input, output, histo, mask); break; - case '8': mk_histo<8>(input, output, histo, mask); break; + case '2': mk_histo<2>(input, quant, histo, proj1, proj2, mask); break; + case '3': mk_histo<3>(input, quant, histo, proj1, proj2, mask); break; + case '4': mk_histo<4>(input, quant, histo, proj1, proj2, mask); break; + case '5': mk_histo<5>(input, quant, histo, proj1, proj2, mask); break; + case '6': mk_histo<6>(input, quant, histo, proj1, proj2, mask); break; + case '7': mk_histo<7>(input, quant, histo, proj1, proj2, mask); break; + case '8': mk_histo<8>(input, quant, histo, proj1, proj2, mask); break; default: usage(); break; } } diff --git a/milena/sandbox/green/tools/annotating/histo/Makefile.am b/milena/sandbox/green/tools/annotating/iz/Makefile.am similarity index 100% copy from milena/sandbox/green/tools/annotating/histo/Makefile.am copy to milena/sandbox/green/tools/annotating/iz/Makefile.am diff --git a/milena/sandbox/green/tools/annotating/iz/iz.cc b/milena/sandbox/green/tools/annotating/iz/iz.cc new file mode 100644 index 0000000..07e5dd9 --- /dev/null +++ b/milena/sandbox/green/tools/annotating/iz/iz.cc @@ -0,0 +1,373 @@ +// TOOLS ==> influence zone transformation + +#include <iostream> +#include <fstream> +#include <boost/format.hpp> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/macros.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/data/compute.hh> + +#include <mln/display/display_histo.hh> + +#include <mln/io/dump/load.hh> +#include <mln/io/dump/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> + +#include <mln/literal/colors.hh> + +#include <mln/labeling/compute.hh> +#include <mln/labeling/mean_values.hh> + +#include <mln/transform/influence_zone_geodesic.hh> + +#include <mln/value/int_u8.hh> + +template <unsigned n> +struct t_labeling_rgbn : mln::Function_v2v< t_labeling_rgbn<n> > +{ + typedef mln::value::rgb<n> t_rgbn; + typedef mln::value::label_8 t_lbl8; + typedef t_rgbn argument; + typedef t_lbl8 result; + typedef mln::image3d<t_lbl8> t_label; + + const t_label& _label; + + t_labeling_rgbn(const t_label& label) : _label(label) {} + + result operator()(const argument& c) const + { + t_lbl8 tmp = mln::opt::at(_label, c.blue(), c.red(), c.green()); + + return tmp; + } +}; + +void compute_stats(const mln::image2d<mln::value::rgb8>& i_input_rgb8, + const mln::image2d<mln::value::label_8>& l_input_lbl8, + const mln::image3d<unsigned>& h_histo_rgbn, + const mln::image3d<mln::value::label_8>& l_histo_lbl8, + const mln::value::label_8& n_labels, + const std::string& log) +{ + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::accu::math::sum<unsigned,unsigned> t_sum; + typedef mln::accu::stat::mean<t_vec3f,t_vec3f,t_vec3f> t_mean; + typedef mln::util::array<unsigned> t_count_array; + typedef mln::util::array<t_vec3f> t_mean_array; + + mln::util::array<float> abs((unsigned)(n_labels)+1); + mln::util::array<float> rel((unsigned)(n_labels)+1); + unsigned nb = 0; + + for (unsigned i = 0; i <= n_labels; ++i) + { + abs[i] = 0.0; + rel[i] = 0.0; + } + + // COMPUTE THE SUM + t_count_array count = mln::labeling::compute(t_sum(), + h_histo_rgbn, + l_histo_lbl8, + n_labels); + + // COMPUTE THE TOTAL + for (unsigned i = 0; i <= n_labels; ++i) + { + unsigned c = count[i]; + nb += c; + } + + // COMPUTE THE PERCENTAGES + for (unsigned i = 0; i <= n_labels; ++i) + if (0 < count[i]) + { + abs[i] = ((float)count[i] / nb)*100.0; + rel[i] = ((float)count[i] / (nb - count[0]))*100.0; + } + + // COMPUTE THE MEAN + + t_mean_array mean = mln::labeling::compute(t_mean(), + i_input_rgb8, + l_input_lbl8, + n_labels); + + // CORRECT 0 LABEL STATS + rel[0] = 0; + mean[0][0] = 255.0; + mean[0][1] = 255.0; + mean[0][2] = 0.0; + + // PRINT STATS + std::ofstream log_stream(log.c_str()); + + for (unsigned i = 0; i <= n_labels; ++i) + { + const t_vec3f& mean_v = mean[i]; + + log_stream << boost::format("%2i|" + "r = %6.2f, g = %6.2f, b = %6.2f |" + "c = %7i, %%i = %5.2f, %%c = %5.2f") + % i + % mean_v[0] + % mean_v[1] + % mean_v[2] + % count[i] + % abs[i] + % rel[i] + << std::endl; + } + + log_stream << std::endl << std::endl; + log_stream.flush(); + log_stream.close(); +} + +bool expect(std::istream& stream, const std::string expected) +{ + bool result; + std::string got; + + stream >> got; + + result = (got == expected); + + return result; +} + +std::istream& operator>>(std::istream& stream, + mln::algebra::vec<3,float>& color) +{ + unsigned lbl; + + stream >> lbl; + if (expect(stream, std::string("|")) && + expect(stream, std::string("r")) && + expect(stream, std::string("="))) + { + stream >> color[0]; + + if (expect(stream, std::string(",")) && + expect(stream, std::string("g")) && + expect(stream, std::string("="))) + { + stream >> color[1]; + + if (expect(stream, std::string(",")) && + expect(stream, std::string("b")) && + expect(stream, std::string("="))) + { + stream >> color[2]; + } + } + } + + return stream; +} + +void load(mln::util::array< mln::algebra::vec<3,float> >& m2_label, + const char *colormap) +{ + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::util::array<t_vec3f> t_mean_array; + + std::ifstream stream(colormap); + std::string buffer; + + getline(stream, buffer); + + while (0 < buffer.size()) + { + std::stringstream line(buffer); + t_vec3f mean_v; + + line >> mean_v; + + m2_label.append(mean_v); + + getline(stream, buffer); + } + + stream.close(); +} + +template<unsigned n> +void mk_iz(const std::string& labeled, // in + const unsigned d, // in + const mln::neighb3d& nbh, // in + const std::string& input, // in + const std::string& quant, // in + const std::string& histo, // in + const std::string& colormap,// in + const std::string& iz, // out + const std::string& proj, // out + const std::string& mean, // out + const std::string& stats) // [out] +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::label_8 t_lbl8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::algebra::vec<3,float> t_v3f; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::image3d<t_lbl8> t_image3d_lbl8; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::stat::mean<t_v3f,t_v3f,t_v3f> t_mean; + typedef mln::util::array<t_v3f> t_mean_array; + + // START OF IMAGE PROCESSING CHAIN + t_image2d_rgb8 i0_input; // input img + t_image2d_rgbn i1_input; // quant img + t_histo3d h1_input; // histo input + t_histo3d h2_input; // histo input + t_image2d_int_u8 p1_histo; // histo proj + t_image2d_rgb8 p3_histo; // histo proj + t_image3d_lbl8 l2_histo; // label histo + t_image3d_lbl8 l3_histo; // iz label hist + t_mean_array m2_label; // colormap + t_mean_array m3_label; // colormap + t_image2d_lbl8 l3_input; // label input + t_image2d_rgb8 i3_mean; // reconstructed + + t_lbl8 n_lbl; // nb labels + t_rgb8 red(mln::literal::red); + + mln::io::dump::load(l2_histo, labeled.c_str()); + mln::io::ppm::load(i0_input, input.c_str()); + mln::io::ppm::load(i1_input, quant.c_str()); + mln::io::dump::load(h1_input, histo.c_str()); + load(m2_label, colormap.c_str()); + + if (0 == d) + { + l3_histo = mln::transform::influence_zone_geodesic(l2_histo, nbh); + } + else + { + l3_histo = mln::transform::influence_zone_geodesic(l2_histo, nbh, d); + } + // END OF IMAGE PROCESSING CHAIN + + // BEGIN DUMPING + + n_lbl = (t_lbl8)(m2_label.size()-1); + + l3_input = mln::data::transform(i1_input, t_labeling_rgbn<n>(l3_histo)); + i3_mean = mln::labeling::mean_values(i0_input, l3_input, n_lbl); + m3_label = mln::labeling::compute(t_mean(), i0_input, l3_input, n_lbl); + + // CORRECT 0 LABEL STATS + m3_label[0][0] = 255.0; + m3_label[0][1] = 255.0; + m3_label[0][2] = 0.0; + + p3_histo = mln::display::display3_histo3d_unsigned<n>(h1_input, + l3_histo, + m3_label, + red); + mln::io::dump::save(l3_input, iz.c_str()); + mln::io::ppm::save(p3_histo, proj.c_str()); + mln::io::ppm::save(i3_mean, mean.c_str()); + + if (0 < stats.size()) + compute_stats(i0_input, l3_input, h1_input, l3_histo, n_lbl, stats); + + // END DUMPING +} + + +void usage() +{ + std::cout << std::endl; + std::cout << "iz labeled.dump d nbh input.ppm q quant.ppm" + << " histo.dump colormap.txt iz.dump proj.ppm" + << " mean.ppm [stats.txt]" << std::endl; + std::cout << std::endl; + std::cout << "where :" << std::endl; + std::cout << "* [ in] labeled.dump is the labeled 3d histogram" << std::endl; + std::cout << "* [ in] d is the depth for the zone influence" + << " transformation (0 means infinite)" << std::endl; + std::cout << "* [ in] nbh is the 3d neighbourhood {6,18,26}" << std::endl; + std::cout << "* [ in] input.ppm is the 8 bits color ppm image" << std::endl; + std::cout << "* [ in] q is the degree of quantification" + << " {2,3,4,5,6,7,8}" << std::endl; + std::cout << "* [ in] quant.ppm is the q bits quantified input" + << " image" << std::endl; + std::cout << "* [ in] histo.dump is the quantified color" + << " histogram" << std::endl; + std::cout << "* [ in] colormap.txt is the colormap for labels" << std::endl; + std::cout << "* [out] iz.dump is the iz labeled 3d histogram" << std::endl; + std::cout << "* [out] proj.ppm is the r/g projection of the" + << " histogram with maxima label plot on" << std::endl; + std::cout << "* [out] mean.ppm is the mean reconstructed image" << std::endl; + std::cout << "* [out] stats.txt is the statistical label report"<< std::endl; + std::cout << std::endl; +} + +int main(int argc, char* args[]) +{ + if (12 == argc || 13 == argc) + { + const std::string labeled(args[1]); // in + const unsigned d = atoi(args[2]); // in + const char nbh = args[3][0]; // in + const std::string input(args[4]); // in + const char q = args[5][0]; // in + const std::string quant(args[6]); // in + const std::string histo(args[7]); // in + const std::string colormap(args[8]); // in + const std::string iz(args[9]); // out + const std::string proj(args[10]); // out + const std::string mean(args[11]); // out + const std::string stats(13 == argc? args[12] : ""); // [out] + + + mln::neighb3d neighbourhood; + + switch (nbh) + { + case '6': neighbourhood = mln::c6(); break; + case '1': neighbourhood = mln::c18(); break; + case '2': neighbourhood = mln::c26(); break; + default: usage(); return 0; // force usage and quit + } + + switch (q) + { + case '2' : mk_iz<2>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '3' : mk_iz<3>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '4' : mk_iz<4>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '5' : mk_iz<5>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '6' : mk_iz<6>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '7' : mk_iz<7>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + case '8' : mk_iz<8>(labeled,d,neighbourhood,input,quant, + histo,colormap,iz,proj,mean,stats);break; + default: usage(); break; + } + } + else + usage(); + + return 0; +} diff --git a/milena/sandbox/green/tools/annotating/opening/opening.cc b/milena/sandbox/green/tools/annotating/opening/opening.cc index 3e1dbf2..cdd37fb 100644 --- a/milena/sandbox/green/tools/annotating/opening/opening.cc +++ b/milena/sandbox/green/tools/annotating/opening/opening.cc @@ -15,36 +15,47 @@ #include <mln/io/dump/load.hh> #include <mln/io/dump/save.hh> -#include <mln/io/pgm/load.hh> +#include <mln/io/ppm/save.hh> #include <mln/io/pgm/save.hh> +#include <mln/literal/colors.hh> + #include <mln/morpho/opening/volume.hh> +#include <mln/value/rgb.hh> #include <mln/value/int_u8.hh> -void mk_opening(const std::string& input, - const unsigned min_vol, - const std::string& output, - const std::string& opened) +template <unsigned n> +void mk_opening(const std::string& histo, // in + const unsigned min_vol, // in + const std::string& opened, // out + const std::string& proj1, // out + const std::string& proj2) // out { typedef mln::value::int_u8 t_int_u8; + typedef mln::value::rgb<n> t_rgbn; typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; typedef mln::image3d<unsigned> t_histo3d; typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; // START OF IMAGE PROCESSING CHAIN t_histo3d h1_input; // histo input t_histo3d h2_input; // histo input - t_image2d_int_u8 p1_histo; // histo proj + t_image2d_int_u8 p1_histo1;// histo proj1 + t_image2d_rgbn p1_histo2;// histo proj2 + t_rgbn red(mln::literal::red); - mln::io::dump::load(h1_input, input.c_str()); + mln::io::dump::load(h1_input, histo.c_str()); h2_input = mln::morpho::opening::volume(h1_input, mln::c6(), min_vol); // END OF IMAGE PROCESSING CHAIN // BEGIN DUMPING - p1_histo = mln::display::display_histo3d_unsigned(h2_input); + p1_histo1 = mln::display::display_histo3d_unsigned(h2_input); + p1_histo2 = mln::display::display3_histo3d_unsigned(h2_input, red); mln::io::dump::save(h2_input, opened.c_str()); - mln::io::pgm::save(p1_histo, output.c_str()); + mln::io::pgm::save(p1_histo1, proj1.c_str()); + mln::io::ppm::save(p1_histo2, proj2.c_str()); // END DUMPING } @@ -52,25 +63,46 @@ void mk_opening(const std::string& input, void usage() { std::cout << std::endl; - std::cout << "opening input.dump v out.dump out.ppm" << std::endl; - std::cout << "where" << std::endl; - std::cout << "input.dump is the 3d color input histo" << std::endl; - std::cout << "v is the minimum size of each composant" << std::endl; - std::cout << "out.pgm is the r/g proj of the opened histogram" << std::endl; - std::cout << "out.dump is the opened histogram" << std::endl; + std::cout << "opening q histo.dump v opened.dump proj.pgm" + << " proj.ppm" << std::endl; + std::cout << std::endl; + std::cout << "where :" << std::endl; + std::cout << "* [ in] q is the degree of quantification" + << " {2,3,4,5,6,7,8}" << std::endl; + std::cout << "* [ in] histo.dump is the quantified color" + << " histogram" << std::endl; + std::cout << "* [ in] v is the minimum size (in pixels) of" + << " each composant" << std::endl; + std::cout << "* [out] opened.dump is the filtered histogram" << std::endl; + std::cout << "* [out] proj.pgm is the r/g projection of the" + << " histogram (summing along the blue axe)" << std::endl; + std::cout << "* [out] proj.ppm is the r/g projection of the" + << " histogram with maxima plot on" << std::endl; std::cout << std::endl; } int main(int argc, char* args[]) { - if (5 == argc) + if (7 == argc) { - const std::string input(args[1]); - const unsigned min_vol = atoi(args[2]); - const std::string output(args[3]); - const std::string opened(args[4]); - - mk_opening(input, min_vol, output, opened); + const char q = args[1][0]; // in + const std::string histo(args[2]); // in + const unsigned min_vol = atoi(args[3]); // in + const std::string opened(args[4]); // out + const std::string proj1(args[5]); // out + const std::string proj2(args[6]); // out + + switch(q) + { + case '2': mk_opening<2>(histo, min_vol, opened, proj1, proj2); break; + case '3': mk_opening<3>(histo, min_vol, opened, proj1, proj2); break; + case '4': mk_opening<4>(histo, min_vol, opened, proj1, proj2); break; + case '5': mk_opening<5>(histo, min_vol, opened, proj1, proj2); break; + case '6': mk_opening<6>(histo, min_vol, opened, proj1, proj2); break; + case '7': mk_opening<7>(histo, min_vol, opened, proj1, proj2); break; + case '8': mk_opening<8>(histo, min_vol, opened, proj1, proj2); break; + default: usage(); break; + } } else usage(); diff --git a/milena/sandbox/green/tools/annotating/regmax/regmax.cc b/milena/sandbox/green/tools/annotating/regmax/regmax.cc index 2079bc4..0ff4d2e 100644 --- a/milena/sandbox/green/tools/annotating/regmax/regmax.cc +++ b/milena/sandbox/green/tools/annotating/regmax/regmax.cc @@ -1,8 +1,13 @@ // TOOLS ==> regmax on histo #include <iostream> +#include <fstream> +#include <boost/format.hpp> #include <mln/accu/stat/histo3d_rgb.hh> +#include <mln/accu/stat/mean.hh> + +#include <mln/algebra/vec.hh> #include <mln/core/macros.hh> #include <mln/core/alias/neighb3d.hh> @@ -18,9 +23,14 @@ #include <mln/io/dump/save.hh> #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> #include <mln/io/ppm/save.hh> #include <mln/labeling/regional_maxima.hh> +#include <mln/labeling/compute.hh> +#include <mln/labeling/mean_values.hh> + +#include <mln/literal/colors.hh> #include <mln/morpho/opening/volume.hh> @@ -28,6 +38,8 @@ #include <mln/value/int_u8.hh> #include <mln/value/rgb8.hh> +#include <mln/util/array.hh> + template <unsigned n> struct t_labeling_rgbn : mln::Function_v2v< t_labeling_rgbn<n> > { @@ -49,19 +61,134 @@ struct t_labeling_rgbn : mln::Function_v2v< t_labeling_rgbn<n> > } }; -void mk_regmax(const std::string& input, - const std::string& quant, - const std::string& histo, - const std::string& label, - const std::string& output) +void compute_stats(const mln::image2d<mln::value::rgb8>& i_input_rgb8, + const mln::image2d<mln::value::label_8>& l_input_lbl8, + const mln::image3d<unsigned>& h_histo_rgbn, + const mln::image3d<mln::value::label_8>& l_histo_lbl8, + const mln::value::label_8& n_labels, + const std::string& log) +{ + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::accu::math::sum<unsigned,unsigned> t_sum; + typedef mln::accu::stat::mean<t_vec3f,t_vec3f,t_vec3f> t_mean; + typedef mln::util::array<unsigned> t_count_array; + typedef mln::util::array<t_vec3f> t_mean_array; + + mln::util::array<float> abs((unsigned)(n_labels)+1); + mln::util::array<float> rel((unsigned)(n_labels)+1); + unsigned nb = 0; + + for (unsigned i = 0; i <= n_labels; ++i) + { + abs[i] = 0.0; + rel[i] = 0.0; + } + + // COMPUTE THE SUM + t_count_array count = mln::labeling::compute(t_sum(), + h_histo_rgbn, + l_histo_lbl8, + n_labels); + + // COMPUTE THE TOTAL + for (unsigned i = 0; i <= n_labels; ++i) + { + unsigned c = count[i]; + nb += c; + } + + // COMPUTE THE PERCENTAGES + for (unsigned i = 0; i <= n_labels; ++i) + if (0 < count[i]) + { + abs[i] = ((float)count[i] / nb)*100.0; + rel[i] = ((float)count[i] / (nb - count[0]))*100.0; + } + + // COMPUTE THE MEAN + + t_mean_array mean = mln::labeling::compute(t_mean(), + i_input_rgb8, + l_input_lbl8, + n_labels); + + // CORRECT 0 LABEL STATS + rel[0] = 0; + mean[0][0] = 255.0; + mean[0][1] = 255.0; + mean[0][2] = 0.0; + + // PRINT STATS + std::ofstream log_stream(log.c_str()); + + for (unsigned i = 0; i <= n_labels; ++i) + { + const t_vec3f& mean_v = mean[i]; + + log_stream << boost::format("%2i|" + "r = %6.2f, g = %6.2f, b = %6.2f |" + "c = %7i, %%i = %5.2f, %%c = %5.2f") + % i + % mean_v[0] + % mean_v[1] + % mean_v[2] + % count[i] + % abs[i] + % rel[i] + << std::endl; + } + + log_stream << std::endl << std::endl; + log_stream.flush(); + log_stream.close(); +} + +void save(mln::util::array< mln::algebra::vec<3,float> >& m2_label, + const char *colormap) +{ + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::util::array<t_vec3f> t_mean_array; + + std::ofstream stream(colormap); + + for (unsigned i = 0; i < m2_label.size(); ++i) + { + const t_vec3f& mean_v = m2_label[i]; + + stream << boost::format("%2i | r = %6.2f, g = %6.2f, b = %6.2f") + % i + % mean_v[0] + % mean_v[1] + % mean_v[2] + << std::endl; + } + + stream.flush(); + stream.close(); +} + +template <unsigned n> +void mk_regmax(const std::string& input, // in + const std::string& quant, // in + const std::string& histo, // in + const std::string& opened, // in + const mln::neighb3d& nbh, // in + const std::string& labeled, // out + const std::string& proj, // out + const std::string& colormap,// out + const std::string& mean, // out + const std::string& stats) // [out] { typedef mln::value::label_8 t_lbl8; typedef mln::value::rgb8 t_rgb8; - typedef mln::value::rgbn t_rgbn; + typedef mln::value::rgb<n> t_rgbn; typedef mln::value::int_u8 t_int_u8; + typedef mln::value::int_u<n> t_int_un; typedef mln::algebra::vec<3,float> t_v3f; typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_int_un> t_image2d_int_un; typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; typedef mln::image3d<t_lbl8> t_image3d_lbl8; typedef mln::image2d<t_lbl8> t_image2d_lbl8; typedef mln::image3d<unsigned> t_histo3d; @@ -71,60 +198,128 @@ void mk_regmax(const std::string& input, t_image2d_rgb8 i0_input; // input img t_image2d_rgbn i1_input; // quant img + t_histo3d h1_input; // input histo t_histo3d h2_input; // opened histo -// t_image2d_int_u8 p2_label; // histo proj - t_image2d_lbl8 p2_label; // histo proj -// t_image2d_rgb8 p2_label; // histo proj + t_image2d_rgb8 p2_label; // histo proj t_image3d_lbl8 l2_histo; // label histo - t_mean_array m2_label; // palette + t_image2d_lbl8 l2_input; // label input + t_mean_array m2_label; // colormap + t_image2d_rgb8 i2_mean; // reconstructed t_lbl8 n_lbl; // nb class + t_rgb8 red(mln::literal::red); + // BEGIN LOADING mln::io::ppm::load(i0_input, input.c_str()); mln::io::ppm::load(i1_input, quant.c_str()); - mln::io::dump::load(h2_input, histo.c_str()); + mln::io::dump::load(h1_input, histo.c_str()); + mln::io::dump::load(h2_input, opened.c_str()); // END LOADING // BEGIN IMAGE PROCESSING - l2_histo = mln::labeling::regional_maxima(h2_input, mln::c6(), n_lbl); + l2_histo = mln::labeling::regional_maxima(h2_input, nbh, n_lbl); // END IMAGE PROCESSING // BEGIN SAVING - mln::debug::println(h2_input); mln::io::dump::save(l2_histo, labeled.c_str()); l2_input = mln::data::transform(i1_input, t_labeling_rgbn<n>(l2_histo)); - m2_label = mln::labeling::compute(t_mean(), i0_input, l2_input, n_labels); - p2_label =mln::display::display3_histo3d_unsigned(h2_input,l2_histo,m2_label); + i2_mean = mln::labeling::mean_values(i0_input, l2_input, n_lbl); + m2_label = mln::labeling::compute(t_mean(), i0_input, l2_input, n_lbl); + + // CORRECT 0 LABEL STATS + m2_label[0][0] = 255.0; + m2_label[0][1] = 255.0; + m2_label[0][2] = 0.0; + + p2_label =mln::display::display3_histo3d_unsigned<n>(h1_input, + l2_histo, + m2_label, + red); + + mln::io::ppm::save(p2_label, proj.c_str()); + save(m2_label, colormap.c_str()); + mln::io::ppm::save(i2_mean, mean.c_str()); + + if (0 < stats.size()) + compute_stats(i0_input, l2_input, h1_input, l2_histo, n_lbl, stats); -// mln::io::pgm::save(p2_label, output.c_str()); - mln::io::ppm::save(p2_label, output.c_str()); - std::cout << "Nb classes : " << n_lbl << std::endl; // END SAVING } - void usage() { std::cout << std::endl; - std::cout << "regmax input.dump out.dump out.ppm" << std::endl; - std::cout << "where" << std::endl; - std::cout << "input.dump is opened histo" << std::endl; - std::cout << "out.pgm is the r/g proj of the opened histogram" << std::endl; - std::cout << "out.dump is the labeled histogram" << std::endl; + std::cout << "regmax input.ppm q quant.ppm histo.dump" + << " opened.dump nbh labeled.dump proj.ppm" + << " colormap.txt mean.ppm [stats.txt]" << std::endl; + std::cout << std::endl; + std::cout << "where :" << std::endl; + std::cout << "* [ in] input.ppm is the 8 bits color ppm image" << std::endl; + std::cout << "* [ in] q is the degree of quantification" + << " {2,3,4,5,6,7,8}" << std::endl; + std::cout << "* [ in] quant.ppm is the q bits quantified input" + << " image" << std::endl; + std::cout << "* [ in] histo.dump is the quantified color" + << " histogram" << std::endl; + std::cout << "* [ in] opened.dump is the filtered histogram" << std::endl; + std::cout << "* [ in] nbh is the 3d neighbourhood {6,18,26}" << std::endl; + std::cout << "* [out] labeled.dump is the labeled 3d histogram" << std::endl; + std::cout << "* [out] proj.ppm is the r/g projection of the" + << " histogram with maxima label plot on" << std::endl; + std::cout << "* [out] colormap.txt is the colormap for labels" << std::endl; + std::cout << "* [out] mean.ppm is the mean reconstructed image" << std::endl; + std::cout << "* [out] stats.txt is the statistical label report"<< std::endl; std::cout << std::endl; } int main(int argc, char* args[]) { - if (4 == argc) + if (11 == argc || 12 == argc) { - const std::string input(args[1]); - const std::string output(args[2]); - const std::string labeled(args[3]); + const std::string input(args[1]); // in + const char q = args[2][0]; // in + const std::string quant(args[3]); // in + const std::string histo(args[4]); // in + const std::string opened(args[5]); // in + const char nbh = args[6][0]; // in + const std::string labeled(args[7]); // out + const std::string proj(args[8]); // out + const std::string colormap(args[9]);// out + const std::string mean(args[10]); // out + const std::string stats(12 == argc? args[11] : ""); // [out] + + + mln::neighb3d neighbourhood; + + switch (nbh) + { + case '6': neighbourhood = mln::c6(); break; + case '1': neighbourhood = mln::c18(); break; + case '2': neighbourhood = mln::c26(); break; + default: usage(); return 0; // force usage and quit + } + + switch (q) + { - mk_regmax(input, output, labeled); + case '2': mk_regmax<2>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '3': mk_regmax<3>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '4': mk_regmax<4>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '5': mk_regmax<5>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '6': mk_regmax<6>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '7': mk_regmax<7>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + case '8': mk_regmax<8>(input,quant,histo,opened,neighbourhood, + labeled,proj,colormap,mean,stats); break; + default: usage(); break; + } } else usage(); -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-212-g4d87541 Fix bugs in the histogram visualization tools.
by green
* green/mln/display/display_histo.cc: Add new vizualisations. * green/mln/display/project_histo.cc: Add new color projections. --- milena/sandbox/ChangeLog | 7 + milena/sandbox/green/mln/display/display_histo.hh | 75 ++++-- milena/sandbox/green/mln/display/project_histo.hh | 341 +++++++++++++-------- 3 files changed, 279 insertions(+), 144 deletions(-) diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index 861e635..7cab790 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,6 +77,13 @@ 2010-02-10 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Fix bugs in the histogram visualization tools. + + * green/mln/display/display_histo.cc: Add new vizualisations. + * green/mln/display/project_histo.cc: Add new color projections. + +2010-02-10 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Fix last details in the image processing chain. * green/tools/annotating/histo/histo.cc: Manage new inputs/outputs. diff --git a/milena/sandbox/green/mln/display/display_histo.hh b/milena/sandbox/green/mln/display/display_histo.hh index 2ba0b61..ef47182 100644 --- a/milena/sandbox/green/mln/display/display_histo.hh +++ b/milena/sandbox/green/mln/display/display_histo.hh @@ -29,12 +29,14 @@ # define MLN_DISPLAY_DISPLAY_HISTO_HH # include <mln/accu/math/sum.hh> +# include <mln/algebra/vec.hh> # include <mln/data/stretch.hh> # include <mln/display/project_histo.hh> # include <mln/fun/v2v/log.hh> # include <mln/value/int_u8.hh> # include <mln/value/rgb8.hh> # include <mln/value/label_8.hh> +# include <mln/util/array.hh> /// \file @@ -57,19 +59,28 @@ namespace mln image2d<value::int_u8> display_histo3d_unsigned(const image3d<unsigned>& histo); - image2d<value::int_u8> - display2_histo3d_unsigned(const image3d<unsigned>& histo); + template <unsigned n> + image2d< value::int_u<n> > + display2_histo3d_unsigned(const image3d<unsigned>& histo, + const value::int_u<n> ambiguous_color); + template <unsigned n> image2d<value::label_8> display2_histo3d_unsigned(const image3d<unsigned>& histo, - const image3d<value::label_8>& label); + const image3d<value::label_8>& label, + const value::label_8 ambiguous_label); - image2d<value::rgb8> - display3_histo3d_unsigned(const image3d<unsigned>& histo); + template <unsigned n> + image2d< value::rgb<n> > + display3_histo3d_unsigned(const image3d<unsigned>& histo, + const value::rgb<n> ambiguous_color); - image2d<value::rgb8> - display3_histo3d_unsigned(const image3d<unsigned>& histo, - const image3d<value::label_8>& label); + template <unsigned n> + image2d< value::rgb8 > + display3_histo3d_unsigned(const image3d<unsigned>& histo, + const image3d<value::label_8>& label, + const util::array< algebra::vec<3,float> >& pal, + const value::rgb8 ambiguous_color); #ifndef MLN_INCLUDE_ONLY @@ -87,7 +98,7 @@ namespace mln /// \parameter[in] histo the histogram in 3d. /// \result return a equivalent 2d image. - + // FIXME : display_shape [in int_u8] image2d<value::int_u8> display_histo3d_unsigned(const image3d<unsigned>& histo) { @@ -102,40 +113,62 @@ namespace mln return proj_int; } - image2d<value::int_u8> - display2_histo3d_unsigned(const image3d<unsigned>& histo) + // FIXME : display_color [in int_un] + template <unsigned n> + image2d< value::int_u<n> > + display2_histo3d_unsigned(const image3d<unsigned>& histo, + const value::int_u<n> ambiguous_color) { - image2d<value::int_u8> proj = project2_histo<0>(histo); + image2d< value::int_u<n> > proj = project2_histo<n,0>(histo, + ambiguous_color); return proj; } + // FIXME : display_label [in label] + template <unsigned n> image2d<value::label_8> display2_histo3d_unsigned(const image3d<unsigned>& histo, - const image3d<value::label_8>& label) + const image3d<value::label_8>& label, + const value::label_8 ambiguous_label) { - image2d<value::label_8> proj = project2_histo<0>(histo, label); + image2d<value::label_8> proj = project2_histo<n,0>(histo, + label, + ambiguous_label); return proj; } - image2d<value::rgb8> - display3_histo3d_unsigned(const image3d<unsigned>& histo) + // FIXME : display_color [in color] + template <unsigned n> + image2d< value::rgb<n> > + display3_histo3d_unsigned(const image3d<unsigned>& histo, + const value::rgb<n> ambiguous_color) { - image2d<value::rgb8> proj = project3_histo<0>(histo); + image2d< value::rgb<n> > proj = project3_histo<n,0>(histo, + ambiguous_color); return proj; } - image2d<value::rgb8> - display3_histo3d_unsigned(const image3d<unsigned>& histo, - const image3d<value::label_8>& label) + + // FIXME : display_label [in color] + template <unsigned n> + image2d< value::rgb8 > + display3_histo3d_unsigned(const image3d<unsigned>& histo, + const image3d<value::label_8>& label, + const util::array<algebra::vec<3,float> >& pal, + const value::rgb8 ambiguous_color) { - image2d<value::rgb8> proj = project3_histo<0>(histo, label); + image2d< value::rgb8 > proj = project3_histo<n,0>(histo, + label, + pal, + ambiguous_color); return proj; } + #endif // ! MLN_INCLUDE_ONLY diff --git a/milena/sandbox/green/mln/display/project_histo.hh b/milena/sandbox/green/mln/display/project_histo.hh index d842c70..30bcd6d 100644 --- a/milena/sandbox/green/mln/display/project_histo.hh +++ b/milena/sandbox/green/mln/display/project_histo.hh @@ -37,12 +37,16 @@ # include <mln/accu/image/take.hh> # include <mln/accu/image/to_result.hh> +# include <mln/algebra/vec.hh> + # include <mln/opt/at.hh> # include <mln/value/int_u8.hh> # include <mln/value/rgb8.hh> # include <mln/value/label_8.hh> +# include <mln/util/array.hh> + /// \file /// /// \brief Allow the visualization of 3d histogram. @@ -60,9 +64,30 @@ namespace mln image2d<mln_result(A)> project_histo(const image3d<V>& histo); - template <typename A, unsigned direction, typename V> + template <typename A, unsigned n, unsigned direction, typename V> image2d<mln_result(A)> - project2_histo(const image3d<V>& histo); + project2_histo(const image3d<V>& histo, + const value::int_u<n>& ambiguous_color); + + template <unsigned n, unsigned direction, typename V> + image2d<V> + project2_histo(const image3d<unsigned>& histo, + const image3d<V>& label); + + template <unsigned n, unsigned direction> + image2d< value::rgb<n> > + project3_histo(const image3d<unsigned>& histo, + const value::rgb<n> ambiguous_color); + + template <unsigned n, unsigned direction> + image2d< value::rgb8 > + project3_histo(const image3d<unsigned>& histo, + const image3d<value::label_8>& label, + const util::array<algebra::vec<3, float> >& pal, + const value::rgb8 ambiguous_color); + // FIXME ==> palette must be 1d-image not an array !! + + # ifndef MLN_INCLUDE_ONLY @@ -96,80 +121,96 @@ namespace mln return accu::image::to_result(histo_accu); } - template <unsigned direction> - image2d<value::int_u8> - project2_histo(const image3d<unsigned>& histo) + // 0 ==> blue + // 1 ==> red + // 2 ==> green + + // mln::opt::at(histo, blue, red, green) + + template <unsigned n, unsigned direction> + image2d< value::int_u<n> > + project2_histo(const image3d<unsigned>& histo, + const value::int_u<n>& ambiguous_color) { - image2d<value::int_u8> result; + image2d< value::int_u<n> > result; if (0 == direction) // blue { - image2d<value::int_u8> arg_max(histo.ncols(), histo.nslices()); + image2d< value::int_u<n> > arg_max(histo.nrows(), histo.ncols()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nslices(); ++i) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - unsigned max = 0; // minimum as possible - signed pos = -1; + unsigned max = 0; // minimum as possible + def::coord pos = -1; - for (unsigned k = 0; k < histo.nrows(); ++k) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = blue; } } - opt::at(arg_max,i,j) = pos; + if (-1 == pos) + opt::at(arg_max,red,green) = ambiguous_color; + else + opt::at(arg_max,red,green) = pos; } result = arg_max; } else if (1 == direction) // red { - image2d<value::int_u8> arg_max(histo.nrows(), histo.nslices()); + image2d< value::int_u<n> > arg_max(histo.ncols(), histo.nslices()); - for (unsigned j = 0; j < histo.nslices(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.ncols(); ++k) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = red; } } - opt::at(arg_max,i,j) = pos; + if (-1 == pos) + opt::at(arg_max,green,blue) = ambiguous_color; + else + opt::at(arg_max,green,blue) = pos; } result = arg_max; } else // 2 == direction // green { - image2d<value::int_u8> arg_max(histo.nrows(), histo.ncols()); + image2d< value::int_u<n> > arg_max(histo.nrows(), histo.nslices()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.nslices(); ++k) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = green; } } - opt::at(arg_max,i,j) = pos; + if (-1 == pos) + opt::at(arg_max,red,blue) = ambiguous_color; + else + opt::at(arg_max,red,blue) = pos; } result = arg_max; @@ -178,81 +219,91 @@ namespace mln return result; } - template <unsigned direction> + template <unsigned n, unsigned direction> image2d<value::label_8> project2_histo(const image3d<unsigned>& histo, - const image3d<value::label_8>& label) + const image3d<value::label_8>& label, + const value::label_8 ambiguous_label) { image2d<value::label_8> result; if (0 == direction) // blue { - image2d<value::label_8> arg_max(histo.ncols(), histo.nslices()); + image2d<value::label_8> arg_max(histo.nrows(), histo.ncols()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nslices(); ++i) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - unsigned max = 0; // minimum as possible - signed pos = -1; + unsigned max = 0; // minimum as possible + def::coord pos = -1; - for (unsigned k = 0; k < histo.nrows(); ++k) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = blue; } } - opt::at(arg_max,i,j) = opt::at(label,i,j,pos); + if (-1 == pos) + opt::at(arg_max,red,green) = ambiguous_label; + else + opt::at(arg_max,red,green) = opt::at(label, pos, red, green); } result = arg_max; } else if (1 == direction) // red { - image2d<value::label_8> arg_max(histo.nrows(), histo.nslices()); + image2d<value::label_8> arg_max(histo.ncols(), histo.nslices()); - for (unsigned j = 0; j < histo.nslices(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.ncols(); ++k) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = red; } } - opt::at(arg_max,i,j) = opt::at(label,pos,i,j); + if (-1 == pos) + opt::at(arg_max,green,blue) = ambiguous_label; + else + opt::at(arg_max,green,blue) = opt::at(label, blue, pos, green); } result = arg_max; } else // 2 == direction // green { - image2d<value::label_8> arg_max(histo.nrows(), histo.ncols()); + image2d<value::label_8> arg_max(histo.nrows(), histo.nslices()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.nslices(); ++k) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = green; } } - opt::at(arg_max,i,j) = opt::at(label,i,pos,j); + if (-1 == pos) + opt::at(arg_max,red,blue) = ambiguous_label; + else + opt::at(arg_max,red,blue) = opt::at(label, blue, red, pos); } result = arg_max; @@ -262,83 +313,117 @@ namespace mln } + + // FIXME ... determine the color of each class. - template <unsigned direction> - image2d<value::rgb8> - project3_histo(const image3d<unsigned>& histo, - const image3d<value::label_8>& label) + // FIXME la palette est supposée en 8 bits + template <unsigned n, unsigned direction> + image2d< value::rgb8 > + project3_histo(const image3d<unsigned>& histo, + const image3d<value::label_8>& label, + const util::array<algebra::vec<3,float> >& pal, + const value::rgb8 ambiguous_color) { - image2d<value::rgb8> result; + image2d< value::rgb8 > result; if (0 == direction) // blue { - image2d<value::rgb8> arg_max(histo.ncols(), histo.nslices()); + image2d< value::rgb8 > arg_max(histo.nrows(), histo.ncols()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nslices(); ++i) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - unsigned max = 0; // minimum as possible - signed pos = -1; + unsigned max = 0; // minimum as possible + def::coord pos = -1; - for (unsigned k = 0; k < histo.nrows(); ++k) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = blue; } } - opt::at(arg_max,i,j) = value::rgb8(i,j,pos); + if (-1 == pos) + opt::at(arg_max,red,green) = ambiguous_color; + else + { + value::int_u8 r = pal[opt::at(label,pos,red,green)][0]; + value::int_u8 g = pal[opt::at(label,pos,red,green)][1]; + value::int_u8 b = pal[opt::at(label,pos,red,green)][2]; + value::rgb8 color(r,g,b); + + opt::at(arg_max,red,green) = color; + } } result = arg_max; } else if (1 == direction) // red { - image2d<value::rgb8> arg_max(histo.nrows(), histo.nslices()); + image2d< value::rgb8 > arg_max(histo.ncols(), histo.nslices()); - for (unsigned j = 0; j < histo.nslices(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.ncols(); ++k) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = red; } } - opt::at(arg_max,i,j) = value::rgb8(pos,i,j); + if (-1 == pos) + opt::at(arg_max,green,blue) = ambiguous_color; + else + { + value::int_u8 r = pal[opt::at(label,blue,pos,green)][0]; + value::int_u8 g = pal[opt::at(label,blue,pos,green)][1]; + value::int_u8 b = pal[opt::at(label,blue,pos,green)][2]; + value::rgb8 color(r,g,b); + + opt::at(arg_max,green,blue) = color; + } } result = arg_max; } else // 2 == direction // green { - image2d<value::rgb8> arg_max(histo.nrows(), histo.ncols()); + image2d< value::rgb8 > arg_max(histo.nrows(), histo.nslices()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.nslices(); ++k) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = green; } } - // FIXME ... how to fix the n of rgb - opt::at(arg_max,i,j) = value::rgb8(i,pos,j); + if (-1 == pos) + opt::at(arg_max,red,blue) = ambiguous_color; + else + { + value::int_u8 r = pal[opt::at(label,blue,red,pos)][0]; + value::int_u8 g = pal[opt::at(label,blue,red,pos)][1]; + value::int_u8 b = pal[opt::at(label,blue,red,pos)][2]; + value::rgb8 color(r,g,b); + + opt::at(arg_max,red,blue) = color; + } } result = arg_max; @@ -347,81 +432,91 @@ namespace mln return result; } - template <unsigned direction> - image2d<value::rgb8> - project3_histo(const image3d<unsigned>& histo) + + template <unsigned n, unsigned direction> + image2d< value::rgb<n> > + project3_histo(const image3d<unsigned>& histo, + const value::rgb<n> ambiguous_color) { - image2d<value::rgb8> result; + image2d< value::rgb<n> > result; if (0 == direction) // blue { - image2d<value::rgb8> arg_max(histo.ncols(), histo.nslices()); + image2d< value::rgb<n> > arg_max(histo.nrows(), histo.ncols()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nslices(); ++i) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - unsigned max = 0; // minimum as possible - signed pos = -1; + unsigned max = 0; // minimum as possible + def::coord pos = -1; - for (unsigned k = 0; k < histo.nrows(); ++k) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = blue; } } - opt::at(arg_max,i,j) = value::rgb8(i,j,pos); + if (-1 == pos) + opt::at(arg_max,red,green) = ambiguous_color; + else + opt::at(arg_max,red,green) = value::rgb<n>(red,green,pos); } result = arg_max; } else if (1 == direction) // red { - image2d<value::rgb8> arg_max(histo.nrows(), histo.nslices()); + image2d< value::rgb<n> > arg_max(histo.ncols(), histo.nslices()); - for (unsigned j = 0; j < histo.nslices(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.ncols(); ++k) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = red; } } - opt::at(arg_max,i,j) = value::rgb8(pos,i,j); + if (-1 == pos) + opt::at(arg_max,green,blue) = ambiguous_color; + else + opt::at(arg_max,green,blue) = value::rgb<n>(pos,green,blue);; } result = arg_max; } else // 2 == direction // green { - image2d<value::rgb8> arg_max(histo.nrows(), histo.ncols()); + image2d< value::rgb<n> > arg_max(histo.nrows(), histo.nslices()); - for (unsigned j = 0; j < histo.ncols(); ++j) - for (unsigned i = 0; i < histo.nrows(); ++i) + for (def::coord blue = 0; blue < (signed)histo.nslices(); ++blue) + for (def::coord red = 0; red < (signed)histo.nrows(); ++red) { unsigned max = 0; // minimum as possible signed pos = -1; - for (unsigned k = 0; k < histo.nslices(); ++k) + for (def::coord green = 0; green < (signed)histo.ncols(); ++green) { - if (max <= opt::at(histo,i,j,k)) + if (max < opt::at(histo,blue,red,green)) { - max = opt::at(histo,i,j,k); - pos = k; + max = opt::at(histo,blue,red,green); + pos = green; } } - // FIXME ... how to fix the n of rgb - opt::at(arg_max,i,j) = value::rgb8(i,pos,j); + if (-1 == pos) + opt::at(arg_max,red,blue) = ambiguous_color; + else + opt::at(arg_max,red,blue) = value::rgb<n>(red,pos,blue); } result = arg_max; -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-214-gb15c1b6 Delete BUG image file in milena/img.
by green
* milena/img/BUG_lean_ascii.pgm.gz: Delete this file. --- milena/img/BUG_lean_ascii.pgm.gz | Bin 75726 -> 0 bytes 1 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 milena/img/BUG_lean_ascii.pgm.gz diff --git a/milena/img/BUG_lean_ascii.pgm.gz b/milena/img/BUG_lean_ascii.pgm.gz deleted file mode 100644 index 7ba3e9d1b02e3a41704448faf72a6c7afe2e8259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75726 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 -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-186-g81d6850 Fix the outside template use case for the convolve macros.
by green
* mln/linear/ch_convolve.hh (mln_ch_convolve_, mln_ch_convolve_grad_): Define new macros to work outside templates. --- milena/ChangeLog | 8 ++++++++ milena/mln/linear/ch_convolve.hh | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletions(-) diff --git a/milena/ChangeLog b/milena/ChangeLog index c7da59f..4f302ce 100644 --- a/milena/ChangeLog +++ b/milena/ChangeLog @@ -534,6 +534,14 @@ * mln/value/rgb.hh: Make use of the new routine to print a correct type names in dump format. +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Fix the outside template use case for the convolve macros. + + * mln/linear/ch_convolve.hh (mln_ch_convolve_, mln_ch_convolve_grad_): + Define new macros to work outside templates. + + 2009-11-18 Guillaume Lazzara <z(a)lrde.epita.fr> * doc/white_paper/white_paper.tex: Fix an invalid URL. diff --git a/milena/mln/linear/ch_convolve.hh b/milena/mln/linear/ch_convolve.hh index ef9fb42..9ea45e5 100644 --- a/milena/mln/linear/ch_convolve.hh +++ b/milena/mln/linear/ch_convolve.hh @@ -1,4 +1,5 @@ -// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory +// (LRDE) // // This file is part of Olena. // @@ -44,9 +45,15 @@ # define mln_ch_convolve(I, W) \ typename mln::linear::ch_convolve<I, W>::ret +# define mln_ch_convolve_(I, W) \ + mln::linear::ch_convolve<I, W>::ret + + # define mln_ch_convolve_grad(I, W) \ typename mln::trait::ch_value< I, algebra::vec< I::site::dim, typename mln::linear::ch_convolve<I,W>::ret::value > >::ret +# define mln_ch_convolve_grad_(I, W) \ + mln::trait::ch_value< I, algebra::vec< I::site::dim, mln::linear::ch_convolve<I,W>::ret::value > >::ret namespace mln -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-187-g224e84a Extend the test image database.
by green
* green/mln/img_path.hh: Add annoting directories to experiment algorithms on fax, handwritten, typed, logo, bill, map, screenshot, slide image. --- milena/sandbox/ChangeLog | 8 ++++++++ milena/sandbox/green/mln/img_path.hh | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 0 deletions(-) diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index 5cb9a9c..56cfd22 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -75,6 +75,14 @@ * bin/pgm_to_pbm.cc, * bin/ppm_negate.cc: New. +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Extend the test image database. + + * green/mln/img_path.hh: Add annoting directories to experiment + algorithms on fax, handwritten, typed, logo, bill, map, screenshot, + slide image. + 2009-11-17 Thierry Geraud <thierry.geraud(a)lrde.epita.fr> Add some bench + canvas + subsampling + browsing code. diff --git a/milena/sandbox/green/mln/img_path.hh b/milena/sandbox/green/mln/img_path.hh index 90cdb49..188b813 100644 --- a/milena/sandbox/green/mln/img_path.hh +++ b/milena/sandbox/green/mln/img_path.hh @@ -33,10 +33,24 @@ #define INIM_IMG_PATH IMG_PATH"/inim" #define INIM_FG_BG_IMG_PATH INIM_IMG_PATH"/fg_bg" #define INIM_IN_IMG_PATH INIM_IMG_PATH"/in" + #define OLENA_IMG_PATH IMG_PATH"/olena" + #define SCRIBO_IMG_PATH IMG_PATH"/scribo" #define SCRIBO_JPG_IMG_PATH SCRIBO_IMG_PATH"/jpg_50p" #define SCRIBO_PGM_IMG_PATH SCRIBO_IMG_PATH"/pgm_50p" #define SCRIBO_PPM_IMG_PATH SCRIBO_IMG_PATH"/ppm_50p" +#define ANNOTING_IMG_PATH IMG_PATH"/annoting" +#define ANNOTING_BILL_IMG_PATH ANNOTING_IMG_PATH"/bill" +#define ANNOTING_FAX_IMG_PATH ANNOTING_IMG_PATH"/fax" +#define ANNOTING_HANDWRITTEN_IMG_PATH ANNOTING_IMG_PATH"/handwritten" +#define ANNOTING_LOGO_IMG_PATH ANNOTING_IMG_PATH"/logo" +#define ANNOTING_MAP_IMG_PATH ANNOTING_IMG_PATH"/map" +#define ANNOTING_SCREENSHOT_IMG_PATH ANNOTING_IMG_PATH"/screenshot" +#define ANNOTING_SLIDE_IMG_PATH ANNOTING_IMG_PATH"/slide" +#define ANNOTING_TYPED_IMG_PATH ANNOTING_IMG_PATH"/typed" + + + #endif // ! MLN_IMG_PATH_HH -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-188-g2f0e113 Works on Millet2008 descriptors.
by green
* green/mln/demo/annoting/bic/Makefile: New Makefile. * green/mln/demo/annoting/bic/bic.cc: New impl. of the bic descriptor. * green/mln/demo/annoting/lep/Makefile: New Makefile. * green/mln/demo/annoting/lep/lep.cc: New impl. of the lep descriptor. * green/mln/demo/annoting/project/Makefile: New Makefile. * green/mln/demo/annoting/project/project.cc: New impl. of the project descriptor. * green/mln/demo/annoting/rgb_64/Makefile: New Makefile. * green/mln/demo/annoting/rgb_64/rgb_64.cc: New impl. of the rgb_64 descriptor. * green/mln/demo/annoting/rgb_64_9/Makefile: New Makefile. * green/mln/demo/annoting/rgb_64_9/rgb_64_9.cc: New impl. of the rgb_64_9 descriptor. * green/mln/demo/annoting/class.txt: New notes on fax class. --- milena/sandbox/ChangeLog | 25 +++ milena/sandbox/green/demo/annoting/bic/Makefile.am | 150 ++++++++++++++ milena/sandbox/green/demo/annoting/bic/bic.cc | 87 ++++++++ milena/sandbox/green/demo/annoting/class.txt | 33 +++ milena/sandbox/green/demo/annoting/lep/Makefile.am | 150 ++++++++++++++ milena/sandbox/green/demo/annoting/lep/lep.cc | 92 +++++++++ .../green/demo/annoting/project/Makefile.am | 150 ++++++++++++++ .../sandbox/green/demo/annoting/project/project.cc | 210 ++++++++++++++++++++ .../sandbox/green/demo/annoting/rgb_64/Makefile.am | 150 ++++++++++++++ .../sandbox/green/demo/annoting/rgb_64/rgb_64.cc | 47 +++++ .../green/demo/annoting/rgb_64_9/Makefile.am | 150 ++++++++++++++ .../green/demo/annoting/rgb_64_9/rgb_64_9.cc | 97 +++++++++ 12 files changed, 1341 insertions(+), 0 deletions(-) create mode 100644 milena/sandbox/green/demo/annoting/bic/Makefile.am create mode 100644 milena/sandbox/green/demo/annoting/bic/bic.cc create mode 100644 milena/sandbox/green/demo/annoting/class.txt create mode 100644 milena/sandbox/green/demo/annoting/lep/Makefile.am create mode 100644 milena/sandbox/green/demo/annoting/lep/lep.cc create mode 100644 milena/sandbox/green/demo/annoting/project/Makefile.am create mode 100644 milena/sandbox/green/demo/annoting/project/project.cc create mode 100644 milena/sandbox/green/demo/annoting/rgb_64/Makefile.am create mode 100644 milena/sandbox/green/demo/annoting/rgb_64/rgb_64.cc create mode 100644 milena/sandbox/green/demo/annoting/rgb_64_9/Makefile.am create mode 100644 milena/sandbox/green/demo/annoting/rgb_64_9/rgb_64_9.cc diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index 56cfd22..f032fcf 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,6 +77,31 @@ 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Works on Millet2008 descriptors. + + * green/mln/demo/annoting/bic/Makefile: New Makefile. + * green/mln/demo/annoting/bic/bic.cc: New impl. of the bic descriptor. + + * green/mln/demo/annoting/lep/Makefile: New Makefile. + * green/mln/demo/annoting/lep/lep.cc: New impl. of the lep descriptor. + + * green/mln/demo/annoting/project/Makefile: New Makefile. + * green/mln/demo/annoting/project/project.cc: New impl. of the project + descriptor. + + * green/mln/demo/annoting/rgb_64/Makefile: New Makefile. + * green/mln/demo/annoting/rgb_64/rgb_64.cc: New impl. of the rgb_64 + descriptor. + + * green/mln/demo/annoting/rgb_64_9/Makefile: New Makefile. + * green/mln/demo/annoting/rgb_64_9/rgb_64_9.cc: New impl. of the + rgb_64_9 descriptor. + + * green/mln/demo/annoting/class.txt: New notes on fax class. + + +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Extend the test image database. * green/mln/img_path.hh: Add annoting directories to experiment diff --git a/milena/sandbox/green/demo/annoting/bic/Makefile.am b/milena/sandbox/green/demo/annoting/bic/Makefile.am new file mode 100644 index 0000000..96eafb4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/bic/Makefile.am @@ -0,0 +1,150 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/milena/sandbox/green/demo/annoting/bic/bic.cc b/milena/sandbox/green/demo/annoting/bic/bic.cc new file mode 100644 index 0000000..7e15970 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/bic/bic.cc @@ -0,0 +1,87 @@ +// Test de l'opérateur de Millet BIC +// Composante RGB sur 6 bits, puis on distingue les points intérieurs des points +// extérieurs. Les points intérieurs ont la même couleur que leur 4-voisinage. +// Puis on construit les 2 histogrammes (intérieur/extérieur) +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/var.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> +#include <mln/data/fill.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/colors.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +int main() +{ + typedef mln::value::rgb<3> t_rgb3; + typedef mln::value::rgb8 t_rgb8; + typedef mln::neighb2d t_neighb2d; + typedef mln::image2d<t_rgb3> t_image2d_rgb3; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::fun::v2v::rgb8_to_rgbn<3> t_rgb8_to_rgb3; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb3 input_rgb3; + t_image2d_rgb3 interior_rgb3; + t_image2d_rgb3 exterior_rgb3; + t_histo3d histo_exterior; + t_histo3d histo_interior; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb3 = mln::data::transform(input_rgb8, t_rgb8_to_rgb3()); + + mln::initialize(interior_rgb3, input_rgb3); + mln::initialize(exterior_rgb3, input_rgb3); + + mln::data::fill(interior_rgb3, mln::literal::black); + mln::data::fill(exterior_rgb3, mln::literal::black); + + mln_piter_(t_image2d_rgb3) p(input_rgb3.domain()); + mln_niter_(t_neighb2d) n(mln::c4(), p); + + for_all(p) + { + bool is_interior = true; + + for_all(n) + is_interior = is_interior && (input_rgb3(p) == input_rgb3(n)); + + if (is_interior) + interior_rgb3(p) = input_rgb3(p); + else + exterior_rgb3(p) = input_rgb3(p); + } + + histo_interior = mln::data::compute(t_histo3d_fun(), interior_rgb3); + histo_exterior = mln::data::compute(t_histo3d_fun(), exterior_rgb3); + + //mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo_interior); + mln::debug::println(histo_exterior); +} diff --git a/milena/sandbox/green/demo/annoting/class.txt b/milena/sandbox/green/demo/annoting/class.txt new file mode 100644 index 0000000..ec7e92e --- /dev/null +++ b/milena/sandbox/green/demo/annoting/class.txt @@ -0,0 +1,33 @@ +Dans scribo les classes de documents sont: + +- image seule (photo) +- courrier manuscrit +- courrier dactylographié +- facture ou feuille organisée sous forme de tableau +- article de journal +- document composite +- infographie (logo, carte, dessin vectoriel) +- slide (copie d'écran ou sauvegarde au format image) + + +Où se situent les faxes ? + + +Description d'un fax: +- ratio l/L assimilable à du A4 ou US Letter +- nombre de couleurs très petit, < 1000 +- plutôt du N & B, mais quelques faxes sont en couleurs +- généralement un % élevé de blanc (la page vierge) + + +Description d'un article de journal: +- nombre élevés de couleurs (> 10 000) en général +- quelques articles de conférences autour de 5000 + + +Description des photos: +- un nombre très élevé de couleurs +- ratio non A4, non US Letter + + + diff --git a/milena/sandbox/green/demo/annoting/lep/Makefile.am b/milena/sandbox/green/demo/annoting/lep/Makefile.am new file mode 100644 index 0000000..96eafb4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/lep/Makefile.am @@ -0,0 +1,150 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/milena/sandbox/green/demo/annoting/lep/lep.cc b/milena/sandbox/green/demo/annoting/lep/lep.cc new file mode 100644 index 0000000..24ee7b4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/lep/lep.cc @@ -0,0 +1,92 @@ +// Test de l'opérateur de Millet LEP +// Composante RGB sur 6 bits, puis sobel 3x3, puis identification configuration +// L'image histogramme des configurations est la résultante + +#include <iostream> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/w_window2d_int.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/linear/ch_convolve.hh> +#include <mln/linear/convolve.hh> +#include <mln/linear/sobel_2d.hh> + +#include <mln/make/w_window2d.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> + +#include <mln/core/var.hh> + + +int main() +{ + using namespace mln; + + typedef mln::w_window2d_int t_win2d; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::int_u<9> t_int_u9; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_int_u9> t_image2d_int_u9; + typedef mln::image2d<int> t_image2d_int; + typedef mln::image2d<unsigned> t_image2d_unsigned; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<double> t_image2d_double; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + + t_image2d_int_u8 input_int_u8; + t_image2d_int input_int; + t_image2d_unsigned input_unsigned; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + t_image2d_float conf_float; + t_image2d_int_u9 conf_int_u9; + t_histo1d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,SCRIBO_PGM_IMG_PATH"/mp00082c_50p_8bits.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(input_int_u8); + + //mln::io::plot::save_image_sh(sobel, "sobel.sh"); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(mln::value::int_u8(), thresh_bool); + //mln::io::plot::save_image_sh(thresh, "thresh.sh"); + + int ws[] = { 1, 2, 4, + 8, 256, 16, + 32, 64, 128 }; + + t_win2d win2d = mln::make::w_window2d(ws); + conf_float = mln::linear::convolve(thresh_int_u8, win2d); + conf_int_u9 = mln::data::convert(t_int_u9(), conf_float); + histo = mln::data::compute(t_histo1d_fun(), conf_int_u9); + + mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/milena/sandbox/green/demo/annoting/project/Makefile.am b/milena/sandbox/green/demo/annoting/project/Makefile.am new file mode 100644 index 0000000..96eafb4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/project/Makefile.am @@ -0,0 +1,150 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/milena/sandbox/green/demo/annoting/project/project.cc b/milena/sandbox/green/demo/annoting/project/project.cc new file mode 100644 index 0000000..e6b25e3 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/project/project.cc @@ -0,0 +1,210 @@ +// Test de l'opérateur de Millet PROJECT +// subsampling the image sur 100x100 +// Sobel, puis seuillage à 100 +// division horizontale de l'image, puis projection verticale des pixels +// division verticale puis projection horizontale +// assemblage des 4 vecteurs de taille 100 + +#include <iostream> +#include <sstream> + +#include <mln/accu/image/init.hh> +#include <mln/accu/image/take.hh> +#include <mln/accu/image/to_result.hh> +#include <mln/accu/stat/mean.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/image/dmorph/unproject_image.hh> + + + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/projection.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/pbm/save.hh> + +#include <mln/linear/sobel_2d.hh> + +#include <mln/opt/at.hh> + +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + + +mln::image1d<mln::value::int_u8> +project_row(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,0> t_projection; + + mln::image1d<t_sum> img_accu(img.ncols()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + +mln::image1d<mln::value::int_u8> +project_col(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,1> t_projection; + + mln::image1d<t_sum> img_accu(img.nrows()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + + +int main() +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::algebra::vec<3,unsigned> t_vec3u; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::accu::meta::stat::mean t_mean_fun; + typedef mln::accu::math::sum<t_int_u8,t_int_u8> t_sum; + typedef mln::image1d<t_int_u8> t_image1d_int_u8; + + t_image2d_int_u8 input_int_u8; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,SCRIBO_PGM_IMG_PATH"/mp00082c_50p_8bits.pgm"); + + + // IMAGE SPLITTING PHASE + t_image2d_int_u8 subimg_int_u8(mln::box2d(mln::point2d(0,0), + mln::point2d(100,100))); + + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 100; + unsigned sz_col = (pmax.col() - pmin.col())/ 100; + + std::cout << domain << std::endl; + + // Subsampling in 100x100 + // FIXME Test that is it a subsampling, not a upsampling! + for (unsigned i = 0; i < 100; ++i) + for (unsigned j = 0; j < 100; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_int_u8 input_part_int_u8(dom); + + mln::data::paste(input_int_u8 | dom, input_part_int_u8); + //mln::io::pgm::save(input_part_int_u8, get_name("output",i,j)); + float mean = mln::data::compute(t_mean_fun(), input_part_int_u8); + t_int_u8 val = static_cast<t_int_u8>(mean); + mln::opt::at(subimg_int_u8, i,j) = val; + } + + mln::io::pgm::save(subimg_int_u8, "subimg.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(subimg_int_u8); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(t_int_u8(), thresh_bool); + + mln::io::pbm::save(thresh_bool, "subimg.pbm"); + + // Define img_up, img_down, img_left, img_right + t_image2d_int_u8 img_up(mln::box2d(mln::point2d(0,0), + mln::point2d(49,99))); + + mln::data::paste(thresh_int_u8 | img_up.domain(), img_up); + + + t_image2d_int_u8 img_down(mln::box2d(mln::point2d(50,0), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_down.domain(), img_down); + + t_image2d_int_u8 img_left(mln::box2d(mln::point2d(0,0), + mln::point2d(99,49))); + + mln::data::paste(thresh_int_u8 | img_left.domain(), img_left); + + + t_image2d_int_u8 img_right(mln::box2d(mln::point2d(0,50), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_right.domain(), img_right); + + + // Define project_up, project_down, project_left, project_right + t_image1d_int_u8 project_up = project_row(img_up); + t_image1d_int_u8 project_down = project_row(img_down); + t_image1d_int_u8 project_left = project_col(img_left); + t_image1d_int_u8 project_right = project_col(img_right); + + mln::io::pgm::save(img_up, "up.pgm"); + mln::io::pgm::save(img_down, "down.pgm"); + mln::io::pgm::save(img_left, "left.pgm"); + mln::io::pgm::save(img_right, "right.pgm"); + + // PRINTING PHASE + + std::cout << std::endl << "UP" << std::endl; + mln::debug::println(project_up); + + std::cout << std::endl << "DOWN" << std::endl; + mln::debug::println(project_down); + + std::cout << std::endl << "LEFT" << std::endl; + mln::debug::println(project_left); + + std::cout << std::endl << "RIGHT" << std::endl; + mln::debug::println(project_right); +} diff --git a/milena/sandbox/green/demo/annoting/rgb_64/Makefile.am b/milena/sandbox/green/demo/annoting/rgb_64/Makefile.am new file mode 100644 index 0000000..96eafb4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/rgb_64/Makefile.am @@ -0,0 +1,150 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/milena/sandbox/green/demo/annoting/rgb_64/rgb_64.cc b/milena/sandbox/green/demo/annoting/rgb_64/rgb_64.cc new file mode 100644 index 0000000..9ff2300 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/rgb_64/rgb_64.cc @@ -0,0 +1,47 @@ +// Test de l'opérateur de Millet RGB-64 +// Composante RGB sur 2 bits, puis histogramme de 64 composantes +// L'image est la résultante + +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/milena/sandbox/green/demo/annoting/rgb_64_9/Makefile.am b/milena/sandbox/green/demo/annoting/rgb_64_9/Makefile.am new file mode 100644 index 0000000..96eafb4 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/rgb_64_9/Makefile.am @@ -0,0 +1,150 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/milena/sandbox/green/demo/annoting/rgb_64_9/rgb_64_9.cc b/milena/sandbox/green/demo/annoting/rgb_64_9/rgb_64_9.cc new file mode 100644 index 0000000..29e90f3 --- /dev/null +++ b/milena/sandbox/green/demo/annoting/rgb_64_9/rgb_64_9.cc @@ -0,0 +1,97 @@ +// Test de l'opérateur de Millet RGB-64 +// Composante RGB sur 2 bits, puis histogramme de 64 composantes +// L'image est la résultante + +#include <iostream> +#include <sstream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm"); + + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_rgb2.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 3; + unsigned sz_col = (pmax.col() - pmin.col())/ 3; + + std::cout << domain << std::endl; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 3; ++i) + for (unsigned j = 0; j < 3; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_rgb2 input_1o9_rgb2(dom); + + mln::data::paste(input_rgb2 | dom, input_1o9_rgb2); + mln::io::ppm::save(input_1o9_rgb2, get_name("output",i,j)); + + histo = mln::data::compute(t_histo3d_fun(), input_1o9_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); + } + +} -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-189-g4b0e1fb Benchmark on distance, preliminary work before optimizing kmean.
by green
* green/bench/transform/distance/Makefile: New Makefile. * green/bench/transform/distance/distance.cc: New processing chain. --- milena/sandbox/ChangeLog | 7 + .../transform/distance}/Makefile.am | 8 +- .../green/bench/transform/distance/distance.cc | 775 ++++++++++++++++++++ 3 files changed, 786 insertions(+), 4 deletions(-) copy milena/sandbox/green/{demo/annoting/project => bench/transform/distance}/Makefile.am (95%) create mode 100644 milena/sandbox/green/bench/transform/distance/distance.cc diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index f032fcf..0850294 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,6 +77,13 @@ 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Benchmark on distance, preliminary work before optimizing kmean. + + * green/bench/transform/distance/Makefile: New Makefile. + * green/bench/transform/distance/distance.cc: New processing chain. + +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Works on Millet2008 descriptors. * green/mln/demo/annoting/bic/Makefile: New Makefile. diff --git a/milena/sandbox/green/demo/annoting/project/Makefile.am b/milena/sandbox/green/bench/transform/distance/Makefile.am similarity index 95% copy from milena/sandbox/green/demo/annoting/project/Makefile.am copy to milena/sandbox/green/bench/transform/distance/Makefile.am index 96eafb4..115c385 100644 --- a/milena/sandbox/green/demo/annoting/project/Makefile.am +++ b/milena/sandbox/green/bench/transform/distance/Makefile.am @@ -7,16 +7,16 @@ ######### INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green -#CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) -CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) #CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) ECHO= echo RM= rm MKDIR= mkdir -p CP= cp -SOURCE_PATTERN= green/demo -BUILD__PATTERN= green/build/demo +SOURCE_PATTERN= green/bench +BUILD__PATTERN= green/build/bench ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) diff --git a/milena/sandbox/green/bench/transform/distance/distance.cc b/milena/sandbox/green/bench/transform/distance/distance.cc new file mode 100644 index 0000000..b71b6df --- /dev/null +++ b/milena/sandbox/green/bench/transform/distance/distance.cc @@ -0,0 +1,775 @@ +// BENCH + +#include <mln/opt/at.hh> + +#include <mln/core/alias/box3d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/alias/point3d.hh> +#include <mln/core/alias/w_window2d_int.hh> +#include <mln/core/alias/w_window3d_int.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/fill.hh> + +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/zero.hh> + +#include <mln/make/w_window2d_int.hh> +#include <mln/make/w_window3d_int.hh> + +#include <mln/transform/influence_zone_geodesic.hh> +#include <mln/transform/influence_zone_front.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> + + +//------------------------------------------------------------------------------ +// Build input, seeds and other tools +//------------------------------------------------------------------------------ + +mln::image2d<mln::value::label_8> build_input2d(unsigned nb_seed, unsigned size) +{ + typedef mln::value::label_8 t_lbl; + mln::image2d<t_lbl> input(mln::box2d(mln::point2d(0,0), + mln::point2d(size,size))); + + mln::data::fill(input, mln::literal::zero); + + unsigned top = size / nb_seed; + unsigned lbl = 0; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + input(mln::point2d(i,j)) = ++lbl; + + //mln::io::pgm::save(input, "input.pgm"); + + return input; +} + +mln::image3d<mln::value::label_8> build_input3d(unsigned nb_seed, unsigned size) +{ + typedef mln::value::label_8 t_lbl; + mln::image3d<t_lbl> input(mln::box3d(mln::point3d(0,0,0), + mln::point3d(size,size,size))); + + mln::data::fill(input, mln::literal::zero); + + unsigned top = size / nb_seed; + unsigned lbl = 0; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + for (unsigned k = top/2; k < size; k += top) + input(mln::point3d(k,i,j)) = ++lbl; + + //mln::io::pgm::save(input, "input.pgm"); + + return input; +} + +mln::util::array<mln::point3d> build_seed3d(unsigned nb_seed, unsigned size) +{ + mln::util::array<mln::point3d> result; + unsigned top = size / nb_seed; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + for (unsigned k = top/2; k < size; k += top) + result.append(mln::point3d(k,i,j)); + + // std::cout << result << std::endl; + + return result; +} + +mln::util::array<mln::point2d> build_seed2d(unsigned nb_seed, unsigned size) +{ + mln::util::array<mln::point2d> result; + unsigned top = size / nb_seed; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + result.append(mln::point2d(j,i)); + + // std::cout << result << std::endl; + + return result; +} + +unsigned distance(mln::point3d p1, mln::point3d p2) +{ + unsigned row2 = (p1.row() - p2.row())*(p1.row() - p2.row()); + unsigned col2 = (p1.col() - p2.col())*(p1.col() - p2.col()); + unsigned sli2 = (p1.sli() - p2.sli())*(p1.sli() - p2.sli()); + unsigned res = row2 + col2 + sli2; + + return res; +} + +unsigned distance(mln::point2d p1, mln::point2d p2) +{ + unsigned row2 = (p1.row() - p2.row())*(p1.row() - p2.row()); + unsigned col2 = (p1.col() - p2.col())*(p1.col() - p2.col()); + unsigned res = row2 + col2; + + return res; +} + + +//------------------------------------------------------------------------------ +// Bench geodesic distance +//------------------------------------------------------------------------------ + +void influence_zone_geodesic_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::neighb2d & neighb) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_geodesic(input, neighb); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + +void influence_zone_geodesic_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::neighb3d & neighb) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_geodesic(input, neighb); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +int test_influence_zone_geodesic_2d() +{ + mln::util::timer timer; + +// Test the label c4 + mln::image1d<float> chart_seed_c04(16*16+2); + + mln::data::fill(chart_seed_c04, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_geodesic_2d(timer,build_input2d(i,256),mln::c4()); + std::cout << "c04|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c04, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c04, "geodesic.c04.2d.seed.sh"); + + // Test the label c8 + mln::image1d<float> chart_seed_c08(16*16+2); + + mln::data::fill(chart_seed_c08, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_geodesic_2d(timer,build_input2d(i,256),mln::c8()); + std::cout << "c08|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c08, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c08, "geodesic.c08.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c04(256+1); + + mln::data::fill(chart_size_c04, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_geodesic_2d(timer, build_input2d(4,i),mln::c4()); + std::cout << "c04|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c04, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c04, "geodesic.c04.2d.size.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c08(256+1); + + mln::data::fill(chart_size_c08, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_geodesic_2d(timer, build_input2d(4,i),mln::c8()); + std::cout << "c08|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c08, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c08, "geodesic.c08.2d.size.sh"); + + + return 0; +} + + +void test_influence_zone_geodesic_3d() +{ + mln::util::timer timer; + + // Test the number of labels c06 + mln::image1d<float> chart_seed_c06(5*5*5+2); + + mln::data::fill(chart_seed_c06, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c6()); + std::cout << "c06|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c06, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c06, "geodesic.c06.3d.seed.sh"); + + // Test the number of labels c18 + mln::image1d<float> chart_seed_c18(5*5*5+2); + + mln::data::fill(chart_seed_c18, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c18()); + std::cout << "c18|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c18, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c18, "geodesic.c18.3d.seed.sh"); + + // Test the number of labels c26 + mln::image1d<float> chart_seed_c26(5*5*5+2); + + mln::data::fill(chart_seed_c26, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c26()); + std::cout << "c26|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c26, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c26, "geodesic.c26.3d.seed.sh"); + + // Test the size of the image c06 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c06(128+1); + + mln::data::fill(chart_size_c06, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c6()); + std::cout << "c06|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c06, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c06, "geodesic.c06.3d.size.sh"); + + // Test the size of the image c18 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c18(128+1); + + mln::data::fill(chart_size_c18, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c18()); + std::cout << "c18|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c18, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c18, "geodesic.c18.3d.size.sh"); + + // Test the size of the image c26 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c26(128+1); + + mln::data::fill(chart_size_c26, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c26()); + std::cout << "c26|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c26, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c26, "geodesic.c26.3d.size.sh"); +} + +//------------------------------------------------------------------------------ +// Bench front distance +//------------------------------------------------------------------------------ + +void influence_zone_front_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::neighb2d& neighb, + const mln::w_window2d_int& w2d) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_front(input, neighb, w2d); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void influence_zone_front_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::neighb3d& neighb, + const mln::w_window3d_int& w3d) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_front(input, neighb, w3d); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void test_influence_zone_front_3d() +{ + mln::util::timer timer; + + int ws_c06[] = { + // Internal slice + 0, 0, 0, + 0, 2, 0, + 0, 0, 0, + // Middle slice + 0, 2, 0, + 2, 0, 2, + 0, 2, 0, + // External slice + 0, 0, 0, + 0, 2, 0, + 0, 0, 0 }; + + int ws_c18[] = { + // Internal slice + 0, 6, 0, + 6, 4, 6, + 0, 6, 0, + // Middle slice + 6, 4, 6, + 4, 0, 4, + 6, 4, 6, + // External slice + 0, 6, 0, + 6, 4, 6, + 0, 6, 0 }; + + int ws_c26[] = { + // Internal slice + 7, 6, 7, + 6, 4, 6, + 7, 6, 7, + // Middle slice + 6, 4, 6, + 4, 0, 4, + 6, 4, 6, + // External slice + 7, 6, 7, + 6, 4, 6, + 7, 6, 7 }; + + mln::w_window3d_int w3d_c06 = mln::make::w_window3d_int(ws_c06); + mln::w_window3d_int w3d_c18 = mln::make::w_window3d_int(ws_c18); + mln::w_window3d_int w3d_c26 = mln::make::w_window3d_int(ws_c26); + + // Test the number of labels c06 + mln::image1d<float> chart_seed_c06(5*5*5+2); + + mln::data::fill(chart_seed_c06, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c6(), w3d_c06); + std::cout << "c06|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c06, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c06, "front.c06.3d.seed.sh"); + + // Test the number of labels c18 + mln::image1d<float> chart_seed_c18(5*5*5+2); + + mln::data::fill(chart_seed_c18, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c18(), w3d_c18); + std::cout << "c18|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c18, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c18, "front.c18.3d.seed.sh"); + + // Test the number of labels c26 + mln::image1d<float> chart_seed_c26(5*5*5+2); + + mln::data::fill(chart_seed_c26, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c26(), w3d_c26); + std::cout << "c26|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c26, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c26, "front.c26.3d.seed.sh"); + + // Test the size of the image c06 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c06(128+1); + + mln::data::fill(chart_size_c06, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c6(), w3d_c06); + std::cout << "c06|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c06, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c06, "front.c06.3d.size.sh"); + + // Test the size of the image c18 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c18(128+1); + + mln::data::fill(chart_size_c18, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c18(), w3d_c18); + std::cout << "c18|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c18, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c18, "front.c18.3d.size.sh"); + + // Test the size of the image c26 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c26(128+1); + + mln::data::fill(chart_size_c26, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c26(), w3d_c26); + std::cout << "c26|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c26, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c26, "front.c26.3d.size.sh"); +} + +void test_influence_zone_front_2d() +{ + mln::util::timer timer; + + int ws_c04[] = { + 0, 2, 0, + 2, 0, 2, + 0, 2, 0}; + + int ws_c08[] = { + 6, 4, 6, + 4, 0, 4, + 6, 4, 6}; + + mln::w_window2d_int w2d_c04 = mln::make::w_window2d_int(ws_c04); + mln::w_window2d_int w2d_c08 = mln::make::w_window2d_int(ws_c08); + + // Test the label c4 + mln::image1d<float> chart_seed_c04(16*16+2); + + mln::data::fill(chart_seed_c04, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_front_2d(timer,build_input2d(i,256),mln::c4(),w2d_c04); + std::cout << "c04|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c04, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c04, "front.c04.2d.seed.sh"); + + // Test the label c8 + mln::image1d<float> chart_seed_c08(16*16+2); + + mln::data::fill(chart_seed_c08, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_front_2d(timer,build_input2d(i,256),mln::c8(),w2d_c08); + std::cout << "c08|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c08, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c08, "front.c08.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c04(256+1); + + mln::data::fill(chart_size_c04, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_front_2d(timer, build_input2d(4,i),mln::c4(),w2d_c04); + std::cout << "c04|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c04, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c04, "front.c04.2d.size.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c08(256+1); + + mln::data::fill(chart_size_c08, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_front_2d(timer, build_input2d(4,i),mln::c8(),w2d_c08); + std::cout << "c08|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c08, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c08, "front.c08.2d.size.sh"); +} + + + + + + +//------------------------------------------------------------------------------ +// Bench euclidian distance +//------------------------------------------------------------------------------ + +void influence_zone_euclidian_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::util::array<mln::point2d>& seed) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + + mln::initialize(output, input); + + mln_piter_(mln::image2d<mln::value::label_8>) p(output.domain()); + mln_eiter_(mln::util::array<mln::point2d>) e(seed); + + for_all(p) + { + unsigned d = 0; + unsigned min_d = mln_max(unsigned); + unsigned min_l = 0; + + for_all(e) + { + d = distance(p, seed(e.index_())); + + if (min_d > d) + { + min_d = d; + min_l = input(e); + } + } + + output(p) = min_l; + } + + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void influence_zone_euclidian_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::util::array<mln::point3d>& seed) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + + mln::initialize(output, input); + + mln_piter_(mln::image3d<mln::value::label_8>) p(output.domain()); + mln_eiter_(mln::util::array<mln::point3d>) e(seed); + + for_all(p) + { + unsigned d = 0; + unsigned min_d = mln_max(unsigned); + unsigned min_l = 0; + + for_all(e) + { + d = distance(p, seed(e.index_())); + + if (min_d > d) + { + min_d = d; + min_l = input(e); + } + } + + output(p) = min_l; + } + + timer.stop(); +} + +void test_influence_zone_euclidian_2d() +{ + mln::util::timer timer; +/* + // Global test + mln::image2d<float> chart(mln::box2d(mln::point2d(0,0), + mln::point2d(16*16+2,256+1))); + + mln::data::fill(chart, mln::literal::zero); + + for (unsigned i = 1; i < 256+1; ++i) // size + for (unsigned j = 1; j < i*i && j < 16+1; ++j) // seed + { + influence_zone_euclidian_2d(timer,build_input2d(j,i),build_seed2d(j,i)); + std::cout << "xxx|" << i << "x" << i << "|" << j << " = " + << timer.read() << std::endl; + mln::opt::at(chart, j*j,i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart, "chart.sh"); +*/ + + // Test the number of labels + mln::image1d<float> chart_seed(16*16+2); + + mln::data::fill(chart_seed, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_euclidian_2d(timer,build_input2d(i,256),build_seed2d(i,256)); + std::cout << "xxx|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed, "euclidian.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size(256+1); + + mln::data::fill(chart_size, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_euclidian_2d(timer, build_input2d(4,i), build_seed2d(4,i)); + std::cout << "xxx|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size, "euclidian.2d.size.sh"); +} + + +void test_influence_zone_euclidian_3d() +{ + mln::util::timer timer; + + // Test the number of labels + mln::image1d<float> chart_seed(5*5*5+2); + + mln::data::fill(chart_seed, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_euclidian_3d(timer,build_input3d(i,128),build_seed3d(i,128)); + std::cout << "xxx|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed, "euclidian.3d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size(128+1); + + mln::data::fill(chart_size, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_euclidian_3d(timer, build_input3d(2,i), build_seed3d(2,i)); + std::cout << "xxx|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size, "euclidian.3d.size.sh"); +} + + +//------------------------------------------------------------------------------ +// Main +// +// Point de fonctionnement du système [image de taille entre 1 et 256] +// Moins de 16 labels +// +//------------------------------------------------------------------------------ + + +int main() +{ + //test_influence_zone_geodesic_3d(); + //test_influence_zone_front_3d(); + //test_influence_zone_euclidian_3d() + +/* + test_influence_zone_euclidian_2d(); + test_influence_zone_front_2d(); + test_influence_zone_geodesic_2d(); +*/ + test_influence_zone_euclidian_3d(); + //test_influence_zone_geodesic_3d(); + //test_influence_zone_front_3d(); + + return 0; +} -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-190-g60fefaa Transform kmean object in a big function and then a canvas.
by green
* green/mln/clustering/kmean_rgb.hh: New library source code. * green/demo/clustering/kmean_rgb/Makefile: New Makefile. * green/demo/clustering/kmean_rgb/kmean_rgb.cc: New demo. code. --- milena/sandbox/ChangeLog | 8 ++ .../histo2d => clustering/kmean_rgb}/Makefile.am | 0 .../green/demo/clustering/kmean_rgb/kmean_rgb.cc | 91 ++++++++++++++++++++ .../sandbox/green/mln/clustering/kmean_rgb.hh | 0 4 files changed, 99 insertions(+), 0 deletions(-) copy milena/sandbox/green/demo/{accu/stat/histo2d => clustering/kmean_rgb}/Makefile.am (100%) create mode 100644 milena/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc copy {scribo => milena}/sandbox/green/mln/clustering/kmean_rgb.hh (100%) diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index 0850294..79f1a7f 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,6 +77,14 @@ 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Transform kmean object in a big function and then a canvas. + + * green/mln/clustering/kmean_rgb.hh: New library source code. + * green/demo/clustering/kmean_rgb/Makefile: New Makefile. + * green/demo/clustering/kmean_rgb/kmean_rgb.cc: New demo. code. + +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Benchmark on distance, preliminary work before optimizing kmean. * green/bench/transform/distance/Makefile: New Makefile. diff --git a/milena/sandbox/green/demo/accu/stat/histo2d/Makefile.am b/milena/sandbox/green/demo/clustering/kmean_rgb/Makefile.am similarity index 100% copy from milena/sandbox/green/demo/accu/stat/histo2d/Makefile.am copy to milena/sandbox/green/demo/clustering/kmean_rgb/Makefile.am diff --git a/milena/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc b/milena/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc new file mode 100644 index 0000000..36641b6 --- /dev/null +++ b/milena/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc @@ -0,0 +1,91 @@ +// DEMO ON KMEAN_RGB + +#include <mln/clustering/kmean_rgb.hh> + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image2d.hh> +//#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/transform.hh> +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/ppm/load.hh> +//#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +//#include <mln/io/ppm/save.hh> +//#include <mln/io/plot/save_image_sh.hh> + +//#include <mln/pw/value.hh> + +//#include <mln/value/int_u8.hh> +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> + +int main() +{ + const std::string& image = OLENA_IMG_PATH"/house.ppm"; + const unsigned k_center = 3; + const unsigned n_times = 10; + const unsigned watch_dog = 10; + + typedef mln::value::label_8 t_lbl8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<5> t_rgb5; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb5> t_image2d_rgb5; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; + + t_image2d_rgb8 house_rgb8; + t_image2d_rgb5 house_rgb5; + t_image2d_lbl8 house_lbl8; + + mln::io::ppm::load(house_rgb8, image.c_str()); + house_rgb5 = mln::data::transform(house_rgb8, t_rgb8_to_rgb5()); + + mln::trace::quiet = false; + + house_lbl8 = mln::clustering::kmean_rgb<double,5>(house_rgb5, + k_center, + watch_dog, + n_times); + mln::trace::quiet = true; + + mln::io::pgm::save(house_lbl8, "label.pgm"); + /* + + + + t_kmean kmean(house_rgb5, k_center, watch_dog, n_times); + + + + //kmean.launch_one_time(); + kmean.launch_n_times(); + + // Not safe because we don't test kmean.is_valid() + + t_kmean::t_color_dbg color_img = kmean.get_color_dbg(); + t_kmean::t_mean_dbg mean_img = kmean.get_mean_dbg(); + t_kmean::t_label_dbg label_img = kmean.get_label_dbg(); + t_kmean::t_variance_cnv variance_cnv = kmean.get_variance_cnv(); + t_kmean::t_mean_cnv mean_cnv = kmean.get_mean_cnv(); + + mln::io::ppm::save(mean_img, "mean.ppm"); + mln::io::ppm::save(color_img, "color.ppm"); + mln::io::pgm::save(label_img, "label.pgm"); + + mln::io::plot::save_image_sh(mean_img, "mean.sh"); + mln::io::plot::save_image_sh(mean_cnv, "mean_cnv.sh"); + mln::io::plot::save_image_sh(variance_cnv, "variance_cnv.sh"); + + */ + + return 0; +} + diff --git a/scribo/sandbox/green/mln/clustering/kmean_rgb.hh b/milena/sandbox/green/mln/clustering/kmean_rgb.hh similarity index 100% copy from scribo/sandbox/green/mln/clustering/kmean_rgb.hh copy to milena/sandbox/green/mln/clustering/kmean_rgb.hh -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-191-g993461b Test experimentation with regmax code on annoting database.
by green
* green/exp/labeling/regional_maxima/Makefile.am: New Makefile. * green/exp/clustering/regional_maxima/regional_maxima.cc: New directory oriented demonstration code. --- milena/sandbox/ChangeLog | 22 ++- .../labeling/regional_maxima}/Makefile.am | 8 +- .../labeling/regional_maxima/regional_maxima.cc | 156 ++++++++++++++++++++ 3 files changed, 175 insertions(+), 11 deletions(-) copy milena/sandbox/green/{demo/annoting/rgb_64 => exp/labeling/regional_maxima}/Makefile.am (95%) create mode 100644 milena/sandbox/green/exp/labeling/regional_maxima/regional_maxima.cc diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index 79f1a7f..b2c3dba 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,38 +77,46 @@ 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Test experimentation with regmax code on annoting database. + + * green/exp/labeling/regional_maxima/Makefile.am: New Makefile. + * green/exp/clustering/regional_maxima/regional_maxima.cc: New directory + oriented demonstration code. + +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Transform kmean object in a big function and then a canvas. * green/mln/clustering/kmean_rgb.hh: New library source code. - * green/demo/clustering/kmean_rgb/Makefile: New Makefile. + * green/demo/clustering/kmean_rgb/Makefile.am: New Makefile. * green/demo/clustering/kmean_rgb/kmean_rgb.cc: New demo. code. 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Benchmark on distance, preliminary work before optimizing kmean. - * green/bench/transform/distance/Makefile: New Makefile. + * green/bench/transform/distance/Makefile.am: New Makefile. * green/bench/transform/distance/distance.cc: New processing chain. 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Works on Millet2008 descriptors. - * green/mln/demo/annoting/bic/Makefile: New Makefile. + * green/mln/demo/annoting/bic/Makefile.am: New Makefile. * green/mln/demo/annoting/bic/bic.cc: New impl. of the bic descriptor. - * green/mln/demo/annoting/lep/Makefile: New Makefile. + * green/mln/demo/annoting/lep/Makefile.am: New Makefile. * green/mln/demo/annoting/lep/lep.cc: New impl. of the lep descriptor. - * green/mln/demo/annoting/project/Makefile: New Makefile. + * green/mln/demo/annoting/project/Makefile.am: New Makefile. * green/mln/demo/annoting/project/project.cc: New impl. of the project descriptor. - * green/mln/demo/annoting/rgb_64/Makefile: New Makefile. + * green/mln/demo/annoting/rgb_64/Makefile.am: New Makefile. * green/mln/demo/annoting/rgb_64/rgb_64.cc: New impl. of the rgb_64 descriptor. - * green/mln/demo/annoting/rgb_64_9/Makefile: New Makefile. + * green/mln/demo/annoting/rgb_64_9/Makefile.am: New Makefile. * green/mln/demo/annoting/rgb_64_9/rgb_64_9.cc: New impl. of the rgb_64_9 descriptor. diff --git a/milena/sandbox/green/demo/annoting/rgb_64/Makefile.am b/milena/sandbox/green/exp/labeling/regional_maxima/Makefile.am similarity index 95% copy from milena/sandbox/green/demo/annoting/rgb_64/Makefile.am copy to milena/sandbox/green/exp/labeling/regional_maxima/Makefile.am index 96eafb4..ab76e18 100644 --- a/milena/sandbox/green/demo/annoting/rgb_64/Makefile.am +++ b/milena/sandbox/green/exp/labeling/regional_maxima/Makefile.am @@ -8,15 +8,15 @@ INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green #CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) -CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) -#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) ECHO= echo RM= rm MKDIR= mkdir -p CP= cp -SOURCE_PATTERN= green/demo -BUILD__PATTERN= green/build/demo +SOURCE_PATTERN= green/exp +BUILD__PATTERN= green/build/exp ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) diff --git a/milena/sandbox/green/exp/labeling/regional_maxima/regional_maxima.cc b/milena/sandbox/green/exp/labeling/regional_maxima/regional_maxima.cc new file mode 100644 index 0000000..28b6b88 --- /dev/null +++ b/milena/sandbox/green/exp/labeling/regional_maxima/regional_maxima.cc @@ -0,0 +1,156 @@ +// EXPERIMENTATION ON REGIONAL MAXIMA +// COUNTING THE IMAGE COLORS + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/math/sum.hh> +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo3d_rgb.hh> +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/variance.hh> + +#include <mln/algebra/vec.hh> + +#include <mln/arith/diff_abs.hh> + +#include <mln/core/macros.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/labeling/regional_maxima.hh> +#include <mln/labeling/mean_values.hh> +#include <mln/labeling/compute.hh> + +#include <mln/literal/colors.hh> + +#include <mln/morpho/opening/volume.hh> +#include <mln/morpho/elementary/dilation.hh> + +#include <mln/opt/at.hh> + +#include <mln/pw/cst.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/rgb.hh> +#include <mln/value/int_u.hh> + + +// LISTER UN REPERTOIRE +// ECRIRE UN FICHIER DE SYNTHESE SUR LA CLASSE +// + + +// n < 8, n is the degree of quantification +template <unsigned n> +unsigned count_image_color(const std::string& image) +{ + typedef mln::value::label_8 t_lbl8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::image3d<t_lbl8> t_image3d_lbl8; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::image2d<unsigned> t_histo2d; + typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::meta::math::count t_count_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + t_histo3d histo; + t_histo3d opened; + t_image3d_lbl8 label; + t_image2d_lbl8 label_img; + t_image3d_lbl8 dilated; + t_lbl8 n_labels; + + mln::io::ppm::load(input_rgb8, image.c_str()); + + unsigned nb_pixel = input_rgb8.ncols() * input_rgb8.nrows(); + unsigned min_volume = (unsigned)(nb_pixel * 0.054); + + input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + // FIXME UTILISER UN PREDICAT COMME FUN::CHESS + histo = mln::data::compute(t_histo3d_fun(), input_rgbn | mln::pw::value(0)); +// return mln::data::compute(t_count_fun(), input_rgbn); + // FIXME REALISER UN COUNT AVEC UN PREDICA SUR L'IMAGE + return nb_pixel; +// opened = mln::morpho::opening::volume(histo, mln::c6(), min_volume); +// label = mln::labeling::regional_maxima(opened, mln::c6(), n_labels); + +// return n_labels; +} + + +int main() +{ + typedef boost::filesystem::path t_path; + //typedef boost::filesystem::initial_path<t_path()> t_init_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path(ANNOTING_TYPED_IMG_PATH); + + std::cout << "entering " << full_path << std::endl; + + if (//1 < argc && + boost::filesystem::exists(full_path) && + boost::filesystem::is_directory(full_path)) + { + boost::filesystem::system_complete(full_path); + const t_iter_path end_iter; + unsigned count = 0; + unsigned sum1 = 0; + unsigned sum2 = 0; + + for (t_iter_path dir_iter(full_path); end_iter != dir_iter; ++dir_iter) + { + unsigned val = count_image_color<5>(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + // FIXME NB COULEURS BRUTES + // FIXME NB DE COULEURS + // FIXME LISTE DES COULEURS + // IMPORTANCES + } + + unsigned mean = sum1 / count; + unsigned var = sum2 / count - mean * mean; + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + + return 0; +} + -- 1.5.6.5
15Â years
1
0
0
0
last-svn-commit-192-ge6a836c 2009-12-02 Yann Jacquelet <jacquelet@lrde.epita.fr>
by green
Optimize regional maxima processing for statistical counts and outputs. * green/demo/labeling/regional_maxima/Makefile.am: Add some compilation directives. * green/demo/labeling/regional_maxima/regional_maxima.cc (t_channel,t_labeling_rgbn,label_image,unquant,print_count2): New. * green/demo/labeling/regional_maxima/regional_maxima.cc (print_count, merge, do_demo) : Update. --- milena/sandbox/ChangeLog | 14 +- .../demo/labeling/regional_maxima/Makefile.am | 2 + .../labeling/regional_maxima/regional_maxima.cc | 661 ++++++++++++++------ 3 files changed, 471 insertions(+), 206 deletions(-) diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog index b2c3dba..89443b8 100644 --- a/milena/sandbox/ChangeLog +++ b/milena/sandbox/ChangeLog @@ -77,10 +77,22 @@ 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Optimize regional maxima processing for statistical counts and outputs. + + * green/demo/labeling/regional_maxima/Makefile.am: Add some compilation + directives. + * green/demo/labeling/regional_maxima/regional_maxima.cc + (t_channel,t_labeling_rgbn,label_image,unquant,print_count2): New. + * green/demo/labeling/regional_maxima/regional_maxima.cc + (print_count, merge, do_demo) : Update. + + +2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + Test experimentation with regmax code on annoting database. * green/exp/labeling/regional_maxima/Makefile.am: New Makefile. - * green/exp/clustering/regional_maxima/regional_maxima.cc: New directory + * green/exp/labeling/regional_maxima/regional_maxima.cc: New directory oriented demonstration code. 2009-12-02 Yann Jacquelet <jacquelet(a)lrde.epita.fr> diff --git a/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am b/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am index 91230b6..1dd1cfb 100644 --- a/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am +++ b/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am @@ -8,6 +8,8 @@ INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O1 -Wall -W -pedantic -ansi -pipe $(INCLUDES) +#CXXFLAGS= -DNDEBUG -O3 -Wall -W -pedantic -ansi -pipe $(INCLUDES) ECHO= echo RM= rm MKDIR= mkdir -p diff --git a/milena/sandbox/green/demo/labeling/regional_maxima/regional_maxima.cc b/milena/sandbox/green/demo/labeling/regional_maxima/regional_maxima.cc index 34801ed..266fbfe 100644 --- a/milena/sandbox/green/demo/labeling/regional_maxima/regional_maxima.cc +++ b/milena/sandbox/green/demo/labeling/regional_maxima/regional_maxima.cc @@ -1,66 +1,257 @@ // DEMO ON REGIONAL MAXIMA -#include <mln/clustering/kmean2d.hh> - #include <iostream> #include <sstream> #include <mln/img_path.hh> -#include <mln/pw/value.hh> -#include <mln/value/label_8.hh> -#include <mln/value/rgb8.hh> +#include <mln/accu/math/sum.hh> +#include <mln/accu/stat/histo3d_rgb.hh> +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/variance.hh> + +#include <mln/algebra/vec.hh> + +// #include <mln/arith/revert.hh> +#include <mln/arith/diff_abs.hh> #include <mln/core/macros.hh> +#include <mln/core/alias/neighb3d.hh> #include <mln/core/image/image2d.hh> #include <mln/core/image/image3d.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +// #include <mln/data/stretch.hh> #include <mln/data/transform.hh> + +// #include <mln/display/display_histo.hh> + #include <mln/fun/v2v/rgb8_to_rgbn.hh> -#include <mln/io/ppm/load.hh> #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> #include <mln/io/ppm/save.hh> -#include <mln/io/plot/save_image_sh.hh> - -#include <mln/accu/stat/histo3d_rgb.hh> -#include <mln/data/compute.hh> +// #include <mln/io/plot/save_image_sh.hh> -#include <mln/arith/revert.hh> #include <mln/labeling/regional_maxima.hh> -#include <mln/core/alias/neighb3d.hh> -#include <mln/core/routine/initialize.hh> +#include <mln/labeling/mean_values.hh> +#include <mln/labeling/compute.hh> + #include <mln/literal/colors.hh> -#include <mln/morpho/watershed/flooding.hh> + +// #include <mln/morpho/watershed/flooding.hh> #include <mln/morpho/opening/volume.hh> #include <mln/morpho/elementary/dilation.hh> -#include <mln/data/stretch.hh> -#include <mln/display/display_histo.hh> -#include <mln/labeling/mean_values.hh> +#include <mln/opt/at.hh> + +// #include <mln/pw/value.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/rgb.hh> +#include <mln/value/int_u.hh> + + +template <unsigned n, unsigned ch> +struct t_channel : mln::Function_v2v< t_channel<n,ch> > +{ + typedef mln::value::rgb<n> t_rgbn; + typedef mln::value::int_u<n> t_int_un; + typedef t_rgbn argument; + typedef t_int_un result; + + result operator()(const argument& c) const + { + result tmp; + + switch(ch) + { + case 0: tmp = c.red(); break; + case 1: tmp = c.green(); break; + case 2: tmp = c.blue(); break; + } + + return tmp; + } +}; + + +// version optimisée de labeling + +template <unsigned n> +struct t_labeling_rgbn : mln::Function_v2v< t_labeling_rgbn<n> > +{ + typedef mln::value::rgb<n> t_rgbn; + typedef mln::value::label_8 t_lbl8; + typedef t_rgbn argument; + typedef t_lbl8 result; + typedef mln::image3d<t_lbl8> t_label; + + const t_label& _label; + + t_labeling_rgbn(const t_label& label) : _label(label) {} + + result operator()(const argument& c) const + { + t_lbl8 tmp = mln::opt::at(_label, c.blue(), c.red(), c.green()); + + return tmp; + } +}; + +// version non optimisée de label + +template <unsigned n> mln::image2d<mln::value::label_8> -label_image(const mln::image2d<mln::value::rgb<5> >& input, - const mln::image3d<mln::value::label_8>& label) +label_image(const mln::image2d< mln::value::rgb<n> >& input, + const mln::image3d< mln::value::label_8>& label) { - mln::image2d<mln::value::label_8> output(input.nrows(), - input.ncols()); + mln::image2d<mln::value::label_8> output; + + initialize(output, input); - mln_piter_(mln::image2d<mln::value::label_8>) po(output.domain()); - mln_piter_(mln::image2d<mln::value::rgb<5> >) pi(input.domain()); + mln_piter(mln::image2d< mln::value::label_8 >) po(output.domain()); + mln_piter(mln::image2d< mln::value::rgb<n> >) pi(input.domain()); for_all_2(po, pi) { - output(po) = label(mln::point3d(input(pi).blue(), - input(pi).red(), - input(pi).green())); + const mln::value::rgb<n>& vi = input(pi); + + output(po) = mln::opt::at(label, vi.blue(), vi.red(), vi.green()); } return output; } +template <unsigned n> +unsigned unquant(const float& value) +{ + unsigned size = pow(2,(8-n)); + unsigned result = value * size; + + return result; +} + +template <unsigned n> +void print_count2(const mln::image2d<mln::value::rgb<n> >& input_rgbn, + const mln::image3d<unsigned>& histo, + const mln::image3d<mln::value::label_8>& label, + const unsigned n_labels) +{ + typedef mln::value::label_8 t_lbl8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::value::int_u<n> t_int_un; + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::accu::math::sum<unsigned,unsigned> t_sum; + typedef mln::accu::stat::mean<t_vec3f,t_vec3f,t_vec3f> t_mean; + typedef mln::accu::math::sum<t_vec3f,t_vec3f> t_diff; + typedef mln::accu::stat::variance<float,float,float> t_var; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image2d<t_int_un> t_image2d_int_un; + + mln::util::array<t_mean> mean((unsigned)(n_labels)+1); +// mln::util::array<t_diff> diff((unsigned)(n_labels)+1); + mln::util::array<t_var> var_red((unsigned)(n_labels)+1); + mln::util::array<t_var> var_green((unsigned)(n_labels)+1); + mln::util::array<t_var> var_blue((unsigned)(n_labels)+1); + mln::util::array<t_sum> count((unsigned)(n_labels)+1); + mln::util::array<float> abs((unsigned)(n_labels)+1); + mln::util::array<float> rel((unsigned)(n_labels)+1); + unsigned nb = 0; + + for (unsigned i = 0; i <= n_labels; ++i) + { + count(i).init(); + mean(i).init(); +// diff(i).init(); + var_red(i).init(); + var_green(i).init(); + var_blue(i).init(); + abs[i] = 0.0; + rel[i] = 0.0; + } + + mln::labeling::compute(count, histo, label, n_labels); + + for (unsigned i = 0; i <= n_labels; ++i) + { + unsigned c = count[i]; + nb += c; + } + + for (unsigned i = 0; i <= n_labels; ++i) + if (0 < count[i]) + { + abs[i] = ((float)count[i] / nb)*100.0; + rel[i] = ((float)count[i] / (nb - count[0]))*100.0; + } + + t_image2d_lbl8 label_img = mln::data::transform(input_rgbn, + t_labeling_rgbn<n>(label)); + + mln::labeling::compute(mean, input_rgbn, label_img, n_labels); + + +// t_image2d_rgbn mean_rgbn = mln::labeling::mean_values(input_rgbn, +// label_img, +// n_labels); +// t_image2d_int_un mean_red =mln::data::transform(mean_rgbn,t_channel<n>()); +// t_image2d_int_un mean_green=mln::data::transform(mean_rgbn,t_channel<n>()); +// t_image2d_int_un mean_blue =mln::data::transform(mean_rgbn,t_channel<n>()); + + t_image2d_int_un input_red =mln::data::transform(input_rgbn, + t_channel<n,0>()); + t_image2d_int_un input_green=mln::data::transform(input_rgbn, + t_channel<n,1>()); + t_image2d_int_un input_blue =mln::data::transform(input_rgbn, + t_channel<n,2>()); + +// FIXME VARIANCE NEGATIVE DANS LES RESULTATS !! + +// mln::labeling::compute(var_red, input_rgbn, label_img, n_labels); +// mln::labeling::compute(var_green, input_rgbn, label_img, n_labels); +// mln::labeling::compute(var_blue, input_rgbn, label_img, n_labels); + +// t_image2d_rgbn diff_rgbn = mln::arith::diff_abs(input_rgbn, mean_rgbn); + + std::cout << mln::labeling::compute(var_red, input_red, label_img, n_labels)(29) << std::endl; +// mln::labeling::compute(t_var(), input_red, label_img, n_labels); + mln::labeling::compute(var_green, input_green, label_img, n_labels); + mln::labeling::compute(var_blue, input_blue, label_img, n_labels); + + for (unsigned i = 0; i <= n_labels; ++i) + { + if (5.0 < abs[i] && 10.0 < rel[i]) + { + const t_vec3f& mean_v = mean[i]; +// const t_vec3f& diff_v = diff[i]; + + std::cout << i << " :[" << unquant<n>(mean_v[0]) +// << "(" << diff_v[0] + << "(" << var_red[i] + << ")," << unquant<n>(mean_v[1]) +// << "(" << diff_v[1] + << "(" << var_green[i] + << ")," << unquant<n>(mean_v[2]) +// << "(" << diff_v[2] + << "(" << var_blue[i] + << ")]- " << count[i] + << " - " << abs[i] + << " - " << rel[i] + << std::endl; + } + } +} void print_count(const mln::image3d<unsigned>& histo, const mln::image3d<mln::value::label_8>& label, @@ -70,66 +261,116 @@ void print_count(const mln::image3d<unsigned>& histo, unsigned red[255]; unsigned green[255]; unsigned blue[255]; + unsigned nb = 0; + unsigned tmp = 0; - for (unsigned i = 0; i < n_labels; ++i) + for (unsigned i = 0; i <= n_labels; ++i) { count[i] = 0; - red[i] = 0.0; - green[i] = 0.0; - blue[i] = 0.0; + red[i] = 0; + green[i] = 0; + blue[i] = 0; } - mln_piter_(mln::image3d<unsigned>) ph(histo.domain()); - mln_piter_(mln::image3d<mln::value::label_8>) pl(label.domain()); + mln_piter_(mln::image3d<unsigned>) p(histo.domain()); - for_all_2(ph, pl) + for_all(p) { - count[label(pl)] += histo(ph); - red[label(pl)] += histo(ph) * pl.row(); - green[label(pl)] += histo(ph) * pl.col(); - blue[label(pl)] += histo(ph) * pl.sli(); + count[label(p)] += histo(p); + red[label(p)] += histo(p) * p.row(); + green[label(p)] += histo(p) * p.col(); + blue[label(p)] += histo(p) * p.sli(); + nb += histo(p); + ++tmp; } std::cout << std::endl; - for (unsigned i = 0; i < n_labels; ++i) - { - red[i] = red[i] / count[i]; - green[i] = green[i] / count[i]; - blue[i] = blue[i] / count[i]; + std::cout << "nb : " << nb << std::endl; + std::cout << "tmp : " << tmp << std::endl; - std::cout << "count[" << i << "](" - << red[i] << ", " << green[i] << ", " - << blue[i] << ") = " << count[i] << std::endl; - } + std::cout << std::endl; + + for (unsigned i = 0; i <= n_labels; ++i) + if (0 < count[i]) + { + float percentage_abs = ((float)count[i] / nb)*100.0; + float percentage_rel = ((float)count[i] / (nb - count[0]))*100.0; + + red[i] = red[i] / count[i]; + green[i] = green[i] / count[i]; + blue[i] = blue[i] / count[i]; + + std::cout << "count[" << i << "](" + << red[i] << ", " << green[i] << ", " + << blue[i] << ") = " << count[i] << "(" + << percentage_abs << "%)"; + + if (0 < i) + std::cout << "[" << percentage_rel << "%]"; + + std::cout << std::endl; + } std::cout << std::endl; } -mln::image2d<mln::value::rgb<5> > -merge(const mln::image2d<mln::value::rgb<5> >& input, - const mln::image3d<mln::value::label_8>& label) +// Version optimisée de merge + +template <unsigned n> +struct t_merge_lbl8_with_rgbn : mln::Function_v2v< t_merge_lbl8_with_rgbn<n> > { - mln::image2d<mln::value::rgb<5> > output; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::value::label_8 t_lbl8; + typedef t_rgbn argument; + typedef t_rgbn result; + typedef mln::image3d<t_lbl8> t_label; + + const t_label& _label; + + t_merge_lbl8_with_rgbn(const t_label& label) : _label(label) {} + + result operator()(const argument& c) const + { + t_rgbn tmp = c; + + if (0 == mln::opt::at(_label, c.blue(), c.red(), c.green())) + { + tmp = mln::literal::black; + } + + return tmp; + } +}; + +// version non optimisée de merge + +template <unsigned n> +mln::image2d< mln::value::rgb<n> > +merge(const mln::image2d< mln::value::rgb<n> >& input, + const mln::image3d< mln::value::label_8 >& label) +{ + mln::image2d<mln::value::rgb<n> > output; mln::initialize(output, input); + // mln::data::fill(output, mln::literal::green); - mln_piter_(mln::image2d<mln::value::rgb<5> >) pi(input.domain()); - mln_piter_(mln::image2d<mln::value::rgb<5> >) po(output.domain()); + mln_piter(mln::image2d< mln::value::rgb<n> >) pi(input.domain()); + mln_piter(mln::image2d< mln::value::rgb<n> >) po(output.domain()); for_all_2(pi, po) { - if (0 < label(mln::point3d(input(pi).blue(), - input(pi).red(), - input(pi).green()))) + const mln::value::rgb<n>& vi = input(pi); + mln::value::rgb<n>& vo = output(po); + + if (0 < mln::opt::at(label,vi.blue(),vi.red(),vi.green())) { - output(po).red() = input(pi).red(); - output(po).green() = input(pi).green(); - output(po).blue() = input(pi).blue(); + vo.red() = vi.red(); + vo.green() = vi.green(); + vo.blue() = vi.blue(); } else - output(po) = mln::literal::red; - //output(po) = mln::literal::black; + vo = mln::literal::black; } return output; @@ -137,78 +378,180 @@ merge(const mln::image2d<mln::value::rgb<5> >& input, // -// Regional maxima image processing chain. -// RGB8 +// Theo regional maxima image processing chain. // -void do_demo(const std::string& image) + +// FIXME C'est la dilatation qui fait apparaître des classes < min_volume. +// Une couleur se dilate au détriment du fond et des autres couleurs. + +int main2() { + const unsigned min_volume = 1000; + //const std::string& image = OLENA_IMG_PATH"/fly.ppm"; + const std::string& image = SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm"; + //const std::string& image = OLENA_IMG_PATH"/tiny.ppm"; + typedef mln::value::label_8 t_lbl8; - typedef mln::value::int_u8 t_int_u8; typedef mln::value::rgb8 t_rgb8; typedef mln::value::rgb<5> t_rgb5; - typedef mln::image3d<t_lbl8> t_image3d_lbl8; - typedef mln::image2d<t_lbl8> t_image2d_lbl8; typedef mln::image2d<t_rgb8> t_image2d_rgb8; typedef mln::image2d<t_rgb5> t_image2d_rgb5; - typedef mln::image2d<t_int_u8> t_image2d_int_u8; - typedef mln::image3d<unsigned> t_histo3d; - typedef mln::image2d<unsigned> t_histo2d; - typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgbn; + typedef mln::image3d<t_lbl8> t_image3d_lbl8; + typedef mln::image3d<unsigned> t_image3d_unsigned; + typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::math::sum<unsigned,unsigned> t_sum; + + mln::util::timer timer; + + // START IMAGE PROCESSING CHAIN + timer.start(); t_image2d_rgb8 input_rgb8; t_image2d_rgb5 input_rgb5; - t_image2d_rgb5 output_rgb5; - t_image2d_rgb5 mean_rgb5; - t_histo3d histo; - t_image2d_int_u8 projected; - t_image2d_int_u8 filtered; - t_histo3d opened; + t_image3d_unsigned histo; + t_image3d_unsigned opened; t_image3d_lbl8 label; - t_image2d_lbl8 label_img; t_image3d_lbl8 dilated; t_lbl8 n_labels; - t_rgb5 value_rgb5; - // IMAGE LOADING PHASE - std::cout << "Image loading phase ..." << std::endl; mln::io::ppm::load(input_rgb8, image.c_str()); - input_rgb5 = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); - mln::io::ppm::save(input_rgb5, "input_rgb5.ppm"); + input_rgb5 = mln::data::transform(input_rgb8, t_rgb8_to_rgb5()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb5); + opened = mln::morpho::opening::volume(histo, mln::c6(), min_volume); + label = mln::labeling::regional_maxima(opened, mln::c6(), n_labels); + dilated = mln::morpho::elementary::dilation(label, mln::c26()); + + mln::util::array<t_sum> length((unsigned)(n_labels)+1); + + for (unsigned i = 0; i <= n_labels; ++i) + length(i).init(); + mln::labeling::compute(length, histo, dilated, n_labels); - // HISTO COMPUTING PHASE - std::cout << "Histo computing phase ..." << std::endl; - histo = mln::data::compute(t_histo3d_fun(), input_rgb5); - projected = mln::display::display_histo3d_unsigned(histo); + timer.stop(); + // STOP IMAGE PROCESSING CHAIN + + std::cout << "Done in " << timer.read() << " ms" << std::endl; + std::cout << "n_labels : " << n_labels << std::endl; + + for (unsigned i = 0; i <= n_labels; ++i) + { + std::cout << "count[" << i << "] = " << length[i] << std::endl; + } + + print_count(histo,label,n_labels); - mln::io::pgm::save(projected, "histo.pgm"); //mln::io::plot::save_image_sh(histo, "histo.sh"); + //mln::io::plot::save_image_sh(histo, "opened.sh"); + //mln::io::plot::save_image_sh(label, "label.sh"); - // HISTO FILTERING PHASE - std::cout << "Histo filtering phase ..." << std::endl;; - opened = mln::morpho::opening::volume(histo, mln::c6(), 1000); - mln::io::plot::save_image_sh(opened, "opened.sh"); - filtered = mln::display::display_histo3d_unsigned(opened); - mln::io::pgm::save(filtered, "filtered.pgm"); + return 0; +} +// n < 8, n is the degree of quantification +template <unsigned n> +void demo() +{ + const unsigned min_volume = 1000; + //const std::string& image = OLENA_IMG_PATH"/fly.ppm"; + const std::string& image = SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm"; + //const std::string& image = OLENA_IMG_PATH"/tiny.ppm"; - // HISTO LABELING PHASE - std::cout << "Histo labeling phase ..." << std::endl; - label = mln::labeling::regional_maxima(opened, mln::c6(), n_labels); - mln::io::plot::save_image_sh(label, "label.sh"); + typedef mln::value::label_8 t_lbl8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::image3d<t_lbl8> t_image3d_lbl8; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::image2d<unsigned> t_histo2d; + typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + mln::util::timer timer; - // HISTO DILATING PHASE - std::cout << "Histo dilating phase ..." << std::endl; - dilated = mln::morpho::elementary::dilation(label, mln::c18()); - mln::io::plot::save_image_sh(dilated, "dilated.sh"); + // START OF IMAGE PROCESSING CHAIN + timer.start(); - // PRINTING PHASE - std::cout << "Labels : " << n_labels << std::endl; - print_count(histo, dilated, n_labels); + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + // t_image2d_rgbn mean_rgb5; + t_histo3d histo; + // t_image2d_int_u8 projected; + // t_image2d_int_u8 filtered; + t_histo3d opened; + t_image3d_lbl8 label; + t_image2d_lbl8 label_img; + t_image3d_lbl8 dilated; + t_lbl8 n_labels; + // t_rgbn value_rgbn; + mln::io::ppm::load(input_rgb8, image.c_str()); + input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + histo = mln::data::compute(t_histo3d_fun(), input_rgbn); + opened = mln::morpho::opening::volume(histo, mln::c6(), min_volume); + label = mln::labeling::regional_maxima(opened, mln::c6(), n_labels); + dilated = mln::morpho::elementary::dilation(label, mln::c26()); + + timer.stop(); + // END OF IMAGE PROCESSING CHAIN + + std::ostringstream name; + std::ostringstream name2; + std::ostringstream name3; + + name << "input_rgb" << n << ".ppm"; + name2 << "output_rgb" << n << ".ppm"; + name3 << "label_img" << n << ".pgm"; + + std::cout << "Done in : " << timer.read() << " s" << std::endl; + std::cout << "Labels : " << n_labels << std::endl; + std::cout << "Name : " << name.str() << std::endl; + + mln::io::ppm::save(input_rgbn, name.str()); + + mln::util::timer timer2; + + timer2.start(); + print_count2(input_rgbn, histo, dilated, n_labels); + timer2.stop(); + std::cout << "timer2 : " << timer2.read() << std::endl; + output_rgbn = mln::data::transform(input_rgbn, + t_merge_lbl8_with_rgbn<n>(label)); + // output_rgbn = merge<n>(input_rgbn, dilated); + mln::io::ppm::save(output_rgbn, name2.str()); + + label_img = mln::data::transform(input_rgbn, + t_labeling_rgbn<n>(label)); + // label_img = label_image<n>(input_rgbn, dilated); + mln::io::pgm::save(label_img, name3.str()); + + + // localiser les couleurs sur l'image (fond en black, le reste) + + // La dilatation englobe beaucoup plus de couleur, mais celles-ci ne + // sont pas forcément présentes dans l'image. Du coup, les classes ne + // bougent pas démeusurément. + +// mln::io::ppm::save(input_rgb5, "input_rgb5.ppm"); +// mln::io::plot::save_image_sh(input_rgb8, "input_rgb8.sh"); +// mln::io::plot::save_image_sh(input_rgb5, "input_rgb5.sh"); +// projected = mln::display::display_histo3d_unsigned(histo); +// mln::io::pgm::save(projected, "histo.pgm"); +// mln::io::plot::save_image_sh(histo, "histo.sh"); +// mln::io::plot::save_image_sh(opened, "opened.sh"); +// filtered = mln::display::display_histo3d_unsigned(opened); +// mln::io::pgm::save(filtered, "filtered.pgm"); +// mln::io::plot::save_image_sh(label, "label.sh"); + // mln::io::plot::save_image_sh(dilated, "dilated.sh"); + + /* // OUTPUT PHASE std::cout << "Output phase ..." << std::endl; output_rgb5 = merge(input_rgb5, dilated); @@ -221,113 +564,21 @@ void do_demo(const std::string& image) label_img = label_image(input_rgb5, dilated); mln::io::pgm::save(label_img, "label_img.pgm"); - // BUILDING MEAN VALUES std::cout << "Building mean values phase ..." << std::endl; mean_rgb5 = mln::labeling::mean_values(input_rgb5, label_img, n_labels); mln::io::ppm::save(mean_rgb5, "mean.ppm"); + */ } -void demo(const std::string& image = SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm", - //const std::string& image = OLENA_IMG_PATH"/house.ppm", - const unsigned k_center = 2, - //const unsigned k_center = 3, - const unsigned n_times = 10, - const unsigned watch_dog = 10) -{ - std::cout << "----------------------------------------" << std::endl; - std::cout << "Launching the demo with these parameters" << std::endl; - std::cout << "image : " << image << std::endl; - std::cout << "k_center : " << k_center << std::endl; - std::cout << "n_times : " << n_times << std::endl; - std::cout << "watch_dog : " << watch_dog << std::endl; - std::cout << "----------------------------------------" << std::endl; - - do_demo(image); -} - -void usage(const int argc, const char *args[]) -{ - std::cout << "----------------------------------------" << std::endl; - std::cout << "argc : " << argc << std::endl; - - for (int i = 0; i < argc; ++i) - std::cout << "args[" << i << "] : " << args[i] << std::endl; - - std::cout << "----------------------------------------" << std::endl; - std::cout << "usage: kmean2d [image [k_center [n_times [watch_dog]]]]" - << std::endl; - std::cout << "pbm image (points to work with)" << std::endl; - std::cout << "unsigned k_center (number of centers)" << std::endl; - std::cout << "unsigned n_times (number of launching)" << std::endl; - std::cout << "unsigned watch_dog (convergence loop)" << std::endl; - std::cout << "----------------------------------------" << std::endl; -} - -bool char_to_unsigned(const bool status, const char *arg, unsigned& val) -{ - bool result = false; - - if (status) - { - std::istringstream arg_stream(arg); - - arg_stream >> val; - - result = !arg_stream.fail(); - } - - return result; -} - -bool char_to_string(const bool status, const char *arg, std::string& val) -{ - bool result = false; - - if (status) - { - std::istringstream arg_stream(arg); - - arg_stream >> val; - - return !arg_stream.fail(); - } - - return result; -} - -int main(const int argc, const char *args[]) +int main() { - std::string image("top"); - unsigned k_center; - unsigned watch_dog; - unsigned n_times; - bool status = true; - - switch (argc) - { - case 5: status = char_to_unsigned(status, args[4], watch_dog); - case 4: status = char_to_unsigned(status, args[3], n_times); - case 3: status = char_to_unsigned(status, args[2], k_center); - case 2: status = char_to_string(status, args[1], image); break; - case 1: status = true; break; - default: status = false; - } - - if (status) - { - switch (argc) - { - case 1: demo(); break; - case 2: demo(image); break; - case 3: demo(image, k_center); break; - case 4: demo(image, k_center, n_times); break; - case 5: demo(image, k_center, n_times, watch_dog); break; - } - } - else - usage(argc, args); - - return 0; + demo<2>(); // 2.26 s + demo<3>(); // 2.29 s + demo<4>(); // 2.29 s + demo<5>(); // 2.37 s + demo<6>(); // 3.19 s + demo<7>(); // 11.43 s + demo<8>(); // 96.19 s } -- 1.5.6.5
15Â years
1
0
0
0
← Newer
1
...
388
389
390
391
392
393
394
...
963
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
Results per page:
10
25
50
100
200