Olena-patches
Threads by month
- ----- 2025 -----
- August
- 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
November 2010
- 7 participants
- 370 discussions
* green/exp/annotating/bench: New directory.
* green/exp/annotating/bench/Makefile.am: New Makefile.
* green/exp/annotating/bench/bench.cc: New source.
---
milena/sandbox/ChangeLog | 8 +
.../annotating/{achromastism => bench}/Makefile.am | 0
milena/sandbox/green/exp/annotating/bench/bench.cc | 1213 ++++++++++++++++++++
3 files changed, 1221 insertions(+), 0 deletions(-)
copy milena/sandbox/green/exp/annotating/{achromastism => bench}/Makefile.am (100%)
create mode 100644 milena/sandbox/green/exp/annotating/bench/bench.cc
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index d86642a..cc4634d 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,5 +1,13 @@
2010-06-21 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+ Benchmark few descriptors.
+
+ * green/exp/annotating/bench: New directory.
+ * green/exp/annotating/bench/Makefile.am: New Makefile.
+ * green/exp/annotating/bench/bench.cc: New source.
+
+2010-06-21 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
Test on image database the achromatism descriptor.
* green/exp/annotating/achromatism/Makefile.am: New Makefile.
diff --git a/milena/sandbox/green/exp/annotating/achromastism/Makefile.am b/milena/sandbox/green/exp/annotating/bench/Makefile.am
similarity index 100%
copy from milena/sandbox/green/exp/annotating/achromastism/Makefile.am
copy to milena/sandbox/green/exp/annotating/bench/Makefile.am
diff --git a/milena/sandbox/green/exp/annotating/bench/bench.cc b/milena/sandbox/green/exp/annotating/bench/bench.cc
new file mode 100644
index 0000000..8e4525f
--- /dev/null
+++ b/milena/sandbox/green/exp/annotating/bench/bench.cc
@@ -0,0 +1,1213 @@
+// BENCH TEST CF MILLET 2008
+
+#include <iostream>
+#include <sstream>
+#include <boost/filesystem.hpp>
+
+#include <mln/algebra/vec.hh>
+
+#include <mln/img_path.hh>
+
+#include <mln/accu/stat/mean.hh>
+#include <mln/accu/stat/histo1d.hh>
+
+#include <mln/arith/minus.hh>
+#include <mln/arith/times.hh>
+#include <mln/arith/diff_abs.hh>
+#include <mln/arith/div.hh>
+
+#include <mln/core/image/image1d.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/core/alias/point1d.hh>
+#include <mln/core/alias/box1d.hh>
+
+#include <mln/data/transform.hh>
+#include <mln/data/compute.hh>
+#include <mln/data/convert.hh>
+#include <mln/data/stretch.hh>
+#include <mln/data/fill.hh>
+
+#include <mln/fun/v2v/component.hh>
+#include <mln/fun/v2v/rgb_to_hue_map.hh>
+#include <mln/fun/v2v/rgb_to_saturation_map.hh>
+#include <mln/fun/v2v/rgb_to_value_map.hh>
+
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/plot/save_image_sh.hh>
+
+#include <mln/literal/zero.hh>
+
+#include <mln/math/ceil.hh>
+#include <mln/math/floor.hh>
+
+#include <mln/opt/at.hh>
+
+#include <mln/trait/value_.hh>
+
+#include <mln/value/rgb8.hh>
+
+
+#include <mln/value/int_u8.hh>
+
+template <typename I>
+mln_value(I) count_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) result = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ result += histo(p);
+
+ return result;
+}
+
+
+template <typename I>
+mln_value(I) sum_frequency_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) sum = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ sum += histo(p);
+
+ return sum;
+}
+
+
+template <typename I>
+mln_coord(mln_site_(I)) peak_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ // Find the peak of the histogram
+ mln_value(I) v_max = mln::opt::at(histo, mln::literal::zero);
+ mln_coord(mln_site_(I)) p_max = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ if (v_max < histo(p))
+ {
+ v_max = histo(p);
+ p_max = p.ind();
+ }
+ }
+
+ return p_max;
+}
+
+
+//============================================================================//
+// MILLET HUE DESCRIPTOR
+//
+// This test is used for discrimination between black and white pictures and
+// color ones. Some colored Black and white pictures have their energy near
+// the peak.
+//============================================================================//
+
+float hue1_descriptor(mln::image1d<unsigned> histo, const short threshold)
+{
+ float cnt1;
+ float cnt2;
+ float prop;
+ short peak;
+ mln::point1d inf;
+ mln::point1d sup;
+
+ peak = peak_histo(histo);
+ inf = mln::point1d(mln::math::max(0, peak-threshold));
+ sup = mln::point1d(mln::math::min(255, peak+threshold));
+ cnt1 = count_histo(histo|mln::box1d(inf,sup));
+ cnt2 = count_histo(histo);
+ prop = ((255.0 * cnt1) / cnt2);
+
+ return prop;
+}
+
+
+//============================================================================//
+// MILLET SATURATION DESCRIPTOR
+//
+// This test is used for discrimination between black and white pictures and
+// color ones. Black and white pictures have their energy in the low saturation
+// band.
+//============================================================================//
+
+float sat1_descriptor(mln::image1d<unsigned> histo, const short threshold)
+{
+ float cnt1;
+ float cnt2;
+ float result;
+
+ cnt1 = count_histo(histo | mln::box1d(mln::point1d(0),
+ mln::point1d(threshold)));
+ cnt2 = count_histo(histo);
+ result = ((255.0 * cnt1) / cnt2);
+
+ return result;
+ color = label_hue(peak);}
+
+
+//============================================================================//
+// MILLET DESCRIPTOR
+//
+// This test aims at compute the number of grey levels. Photographies tends to
+// use all the levels or many of them.
+//============================================================================//
+
+template <typename I>
+mln_value(I) count_null_frequency_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) count = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ if (0 == histo(p))
+ count++;
+
+ return count;
+}
+
+
+float lvl0_descriptor(mln::image1d<unsigned> histo)
+{
+ float result;
+
+ // FIXME 255
+ result = 255-count_null_frequency_histo(histo);
+
+ return result;
+}
+
+//============================================================================//
+// DENSITY DESCRIPTOR
+//
+//
+//============================================================================//
+
+template <typename I>
+float cmp_equi_frequency_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ float sum = 0;
+ float var = 0;
+
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ sum ++;
+ var += mln::math::sqr(histo(p) - (1/256.0));
+ }
+
+ var = var / sum;
+
+ return var;
+}
+
+float hue0_descriptor(mln::image1d<unsigned> histo)
+{
+ mln::image1d<float> histo_float;
+ float sum;
+ float result;
+
+ sum = sum_frequency_histo(histo);
+ histo_float = mln::data::convert(float(), histo) / sum;
+ result = cmp_equi_frequency_histo(histo_float);
+
+ return result*255;
+}
+
+
+float sat0_descriptor(mln::image1d<unsigned> histo)
+{
+ mln::image1d<float> histo_float;
+ float sum;
+ float result;
+
+ sum = sum_frequency_histo(histo);
+ histo_float = mln::data::convert(float(), histo) / sum;
+ result = cmp_equi_frequency_histo(histo_float);
+
+ return result*255;
+}
+
+float val0_descriptor(mln::image1d<unsigned> histo)
+{
+ mln::image1d<float> histo_float;
+ float sum;
+ float result;
+
+ sum = sum_frequency_histo(histo);
+ histo_float = mln::data::convert(float(), histo) / sum;
+ result = cmp_equi_frequency_histo(histo_float);
+
+ return result*255;
+}
+
+//============================================================================//
+// MILLET DESCRIPTOR
+//
+// This test aims at compute some deviation on the peak of the histogram of
+// the image. Large deviations mean lots of graduation in colors (such as
+// photos) and small ones mean something like cartoon.
+//============================================================================//
+
+
+// calcul de contribution
+float r(short p, unsigned histo_p, short x, unsigned histo_x)
+{
+ float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p));
+
+ return result;
+}
+
+template <typename I>
+float stddev3(const mln::Image<I>& histo_, unsigned peak)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ // Compute stddev
+
+ float stddev = 0.0;
+
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ stddev += r((short)peak, mln::opt::at(histo,peak), p.ind(), histo(p));
+ }
+
+ return stddev;
+}
+
+template <typename I>
+float stddev2(const mln::Image<I>& histo_, unsigned peak, unsigned limit)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ float stddev_low = 0.0;
+ float stddev_up = 0.0;
+ float ret = 0.0;
+
+ // A transformer avec des iterators
+
+ if (250 > peak)
+ stddev_up = stddev3(histo |mln::box1d(mln::point1d(peak+1),
+ mln::point1d(peak+limit)), peak);
+
+ if (5 < peak)
+ stddev_low = stddev3(histo |mln::box1d(mln::point1d(peak-limit),
+ mln::point1d(peak-1)), peak);
+
+ ret = (250 < peak)? stddev_low : (5 > peak)? stddev_up :
+ (stddev_low + stddev_up)/2;
+
+ return ret;
+}
+
+float var0_descriptor(mln::image1d<unsigned> histo, const short threshold)
+{
+ typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_map;
+
+ float result;
+ short peak;
+
+ peak = peak_histo(histo);
+ result = stddev2(histo, peak, threshold);
+
+ return result;
+}
+
+
+//============================================================================//
+// ERROR DESCRIPTOR
+//============================================================================//
+
+
+float err_descriptor(mln::image2d<mln::value::int_u8> r_img_map,
+ mln::image2d<mln::value::int_u8> g_img_map,
+ mln::image2d<mln::value::int_u8> b_img_map,
+ mln::image2d<mln::value::int_u8> r_rdc_map,
+ mln::image2d<mln::value::int_u8> g_rdc_map,
+ mln::image2d<mln::value::int_u8> b_rdc_map)
+
+
+{
+ typedef mln::accu::meta::stat::mean t_mean;
+ typedef mln::image2d<mln::value::int_u8> t_map;
+ typedef mln_trait_op_minus_(t_map,t_map) t_minus;
+ typedef mln_trait_op_times_(t_minus,t_minus) t_times;
+
+
+ t_minus minus_red;
+ t_minus minus_green;
+ t_minus minus_blue;
+
+ t_times times_red;
+ t_times times_green;
+ t_times times_blue;
+
+ float error_red;
+ float error_green;
+ float error_blue;
+
+ float error;
+
+ minus_red = (r_img_map - r_rdc_map);
+ times_red = minus_red * minus_red;
+
+ minus_green = (g_img_map - g_rdc_map);
+ times_green = minus_green * minus_green;
+
+ minus_blue = (b_img_map - b_rdc_map);
+ times_blue = minus_blue * minus_blue;
+
+ error_red = mln::data::compute(t_mean(), times_red);
+ error_green = mln::data::compute(t_mean(), times_green);
+ error_blue = mln::data::compute(t_mean(), times_blue);
+
+ error = (error_red + error_green + error_blue)/3.0;
+ error = mln::math::sqrt(error);
+ error = 20 * log(255/error);
+
+// Le PNSNR semble offrir plus d'espace pour la discrimination
+// Si les images sont identiques ==> PNSNR = +inf
+// Si les images sont très différentes ==> PNSNR = 0
+ // FIXME METTRE UN MAX A 255
+
+ return error;
+}
+
+
+
+//============================================================================//
+// CLASSIFICATION DE FISHER EN 2 CLASSES SUR UN HISTO 1D
+//============================================================================//
+
+template <typename I>
+mln_value(I) cnt_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) sum = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ sum += histo(p);
+ }
+
+ return sum;
+}
+
+template <typename I>
+mln_value(I) sum_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) pos = mln::literal::zero;
+ mln_value(I) sum = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ pos = p.ind();
+ sum += pos*histo(p);
+ }
+
+ return sum;
+}
+
+template <typename I>
+mln_value(I) avg_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) pos = mln::literal::zero;
+ mln_value(I) sum = mln::literal::zero;
+ mln_value(I) cnt = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ pos = p.ind();
+ cnt += histo(p);
+ sum += pos*histo(p);
+ }
+
+ return (0 == cnt)? 0 : sum/cnt;
+}
+
+template <typename I>
+mln_value(I) var3_histo(const mln::Image<I>& histo_, float mean)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) pos = mln::literal::zero;
+ mln_value(I) sum = mln::literal::zero;
+ mln_value(I) cnt = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ cnt += histo(p);
+ sum += (mln::math::sqr(p.ind()-mean)*histo(p));
+ }
+
+ return (0 == cnt)? 0 : sum/cnt;
+}
+
+template <typename I>
+mln_value(I) var2_histo(const mln::Image<I>& histo_, float mean)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) pos = mln::literal::zero;
+ mln_value(I) sum = mln::literal::zero;
+ mln_value(I) sqr = mln::literal::zero;
+ mln_value(I) cnt = mln::literal::zero;
+ mln_value(I) dlt = mln::literal::zero;
+ mln_value(I) mxt = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ pos = p.ind();
+ cnt += (histo(p));
+ sum += (histo(p)*pos);
+ mxt += (histo(p)*pos*mean);
+ sqr += (histo(p)*mln::math::sqr(pos));
+ dlt += (histo(p)*mln::math::sqr(pos - mean));
+
+ std::cout << "p = " << pos << std::endl;
+ std::cout << "p² = " << mln::math::sqr(pos) << std::endl;
+ std::cout << "p*mean = " << (pos*mean) << std::endl;
+ std::cout << "p-mean = " << (pos-mean) << std::endl;
+ std::cout << "(p-mean)² = " << mln::math::sqr(pos-mean) << std::endl;
+ std::cout << "histo(p) = " << histo(p) << std::endl;
+ std::cout << "histo(p)*p = " << (pos*histo(p)) << std::endl;
+ std::cout << "histo(p)*p²= " << (mln::math::sqr(pos)*histo(p))
+ << std::endl;
+ std::cout << "cnt = " << cnt << std::endl;
+ std::cout << "sum = " << sum << std::endl;
+ std::cout << "sqr = " << sqr << std::endl;
+ std::cout << "dlt = " << dlt << std::endl;
+ std::cout << "mxt = " << mxt << std::endl;
+ std::cout << std::endl;
+ }
+
+ std::cout << "sqr/cnt = " << (sqr/cnt) << std::endl;
+ std::cout << "sum/cnt = " << (sum/cnt) << std::endl;
+ std::cout << "(sum/cnt)² = " << mln::math::sqr(sum/cnt) << std::endl;
+ std::cout << "dlt/cnt = " << dlt/cnt << std::endl;
+ std::cout << "mxt/cnt = " << mxt/cnt << std::endl;
+ std::cout << std::endl;
+
+ std::cout << "sqr = "
+ << (sqr) << std::endl;
+ std::cout << "dlt = "
+ << (dlt) << std::endl;
+ std::cout << "cnt*avg² = "
+ << (mln::math::sqr(sum/cnt)*cnt) << std::endl;
+ std::cout << "2*mxt = "
+ << (2*mxt) << std::endl;
+ std::cout << "sqr - cnt*avg² = "
+ << (sqr/cnt - mln::math::sqr(sum/cnt)) << std::endl;
+ std::cout << "(sqr -2*mxt + cnt*avg²) = "
+ << ((sqr - 2*mxt + mln::math::sqr(sum/cnt))/cnt) << std::endl;
+ std::cout << std::endl;
+ return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt);
+}
+
+template <typename I>
+mln_value(I) var_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) pos = mln::literal::zero;
+ mln_value(I) sum = mln::literal::zero;
+ mln_value(I) sqr = mln::literal::zero;
+ mln_value(I) cnt = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ pos = p.ind();
+ cnt += (histo(p));
+ sum += (histo(p)*pos);
+ sqr += (histo(p)*mln::math::sqr(pos));
+ }
+
+ return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt);
+}
+
+//============================================================================//
+// CLASSIFIEUR
+//============================================================================//
+
+
+// Linear discriminant analysis in 1d
+// With same variance, threshold = (m1+m2)/2
+// With different variance, (m1*sqrt(v1)+m2*sqrt(v2))/(sqrt(v1)+sqrt(v2))
+float threshold_histo(float avg1, float var1, float avg2, float var2)
+{
+ float sigma1 = mln::math::sqrt(var1);
+ float sigma2 = mln::math::sqrt(var2);
+ float threshold = (avg1*sigma1+avg2*sigma2)/(sigma1+sigma2);
+
+ return threshold;
+}
+
+float threshold3_histo(float avg1, float var1, float avg2, float var2)
+{
+ float threshold = (avg1*var1+avg2*var2)/(var1+var2);
+
+ return threshold;
+}
+
+
+// if gaussian without the same variance
+float threshold2_histo(float avg1, float var1, float avg2, float var2)
+{
+ float a = var2 - var1;
+ float b = -2 * (avg1 * var2 - avg2 * var1);
+ float c = var2 * mln::math::sqr(avg1) - var1 * mln::math::sqr(avg2);
+ float d = mln::math::sqr(b) - 4 * a * c;
+
+ if (d < 0)
+ std::cout << "delta negatif" << std::endl;
+
+ float x1 = (-b+mln::math::sqrt(d))/(2*a);
+ float x2 = (-b-mln::math::sqrt(d))/(2*a);
+
+ std::cout << "a = " << a << std::endl;
+ std::cout << "b = " << b << std::endl;
+ std::cout << "c = " << c << std::endl;
+ std::cout << "d = " << d << std::endl;
+ std::cout << "x1 = " << x1 << std::endl;
+ std::cout << "x2 = " << x2 << std::endl;
+
+ return x2;
+}
+
+template <typename I>
+mln_value(I) sqr_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ mln_value(I) sum = mln::literal::zero;
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ sum += (mln::math::sqr(p.ind())*histo(p));
+
+ return sum;
+}
+
+
+short min_error(const mln::image1d<float> histo_grp1,
+ const mln::image1d<float> histo_grp2,
+ float *_c00, float *_c10, float *_c01, float *_c11)
+{
+ float c00[256];
+ float c10[256];
+ float c01[256];
+ float c11[256];
+ float err[256];
+
+ for (short p = 0; p < 256; p++)
+ {
+ c00[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(0),
+ mln::point1d(p)));
+
+ c10[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(p+1),
+ mln::point1d(255)));
+
+ c01[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(0),
+ mln::point1d(p)));
+
+ c11[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(p+1),
+ mln::point1d(255)));
+ }
+
+ short threshold = 0;
+ float error = c01[0] + c01[0] + c00[0] + c11[0];
+
+ for(short p = 0; p < 256; p++)
+ {
+ err[p] = c10[p] + c01[p];
+
+// std::cout << " p = " << p
+// << ";c00 = " << c00[p]
+// << ";c10 = " << c10[p]
+// << ";c01 = " << c01[p]
+// << ";c11 = " << c11[p]
+// << std::endl;
+// std::cout << "err[" << p << "] = " << err[p] << std::endl;
+
+ if (error > err[p])
+ {
+ error = err[p];
+ threshold = p;
+ }
+ }
+
+ *_c00 = c00[threshold];
+ *_c10 = c10[threshold];
+ *_c01 = c01[threshold];
+ *_c11 = c11[threshold];
+
+ return threshold;
+}
+
+// return the threshold
+short fisher_analysis(const mln::image1d<float> histo)
+{
+ typedef mln::value::int_u8 t_int_u8;
+
+ // FIXE ME SIZE const short a = mln_min(t_int_u8);
+ // float cnt1[a];
+
+ float cnt1[256];
+ float sum1[256];
+ float sqr1[256];
+ float avg1[256];
+ float var1[256];
+
+ float cnt2[256];
+ float sum2[256];
+ float sqr2[256];
+ float avg2[256];
+ float var2[256];
+
+ float cnt0[256]; // count of points
+ float sum0[256]; // sum of population
+ float sqr0[256]; // sqr of population
+ float avg0[256]; // average of the population
+ float v_in[256]; // variance with-in class
+ float v_bw[256]; // variance between class
+ float var0[256]; // variance of the population
+ short threshold;
+ float pos;
+ float min_var;
+
+ // Assign the first elements
+ cnt1[0] = 0;
+ sum1[0] = 0;
+ sqr1[0] = 0;
+ avg1[0] = 0;
+ var1[0] = 0;
+
+ // Compute the stats of the wall histogram
+ cnt2[0] = 0;
+ sum2[0] = 0;
+ sqr2[0] = 0;
+
+ for(short p = 0; p < 256; p++)
+ {
+ pos = p;
+ cnt2[0] += mln::opt::at(histo,p);
+ sum2[0] += (pos * mln::opt::at(histo,p));
+ sqr2[0] += (mln::math::sqr(pos) * mln::opt::at(histo,p));
+ }
+
+ avg2[0] = (0 == cnt2[0])? 0 : sum2[0] / cnt2[0];
+ var2[0] = (0 == cnt2[0])? 0 : sqr2[0] / cnt2[0] - mln::math::sqr(avg2[0]);
+
+ // watch the array limits
+ for (short p = 1; p < 256; p++)
+ {
+ pos = p;
+
+ // Assign the statistics to the primary class
+ cnt1[p] = cnt1[p-1] + mln::opt::at(histo, p);
+ sum1[p] = sum1[p-1] + pos * mln::opt::at(histo, p);
+ sqr1[p] = sqr1[p-1] + mln::math::sqr(pos) * mln::opt::at(histo, p);
+ avg1[p] = (0 == cnt1[p])? 0 : (sum1[p] / cnt1[p]);
+ var1[p] = (0 == cnt1[p])? 0 : ((sqr1[p] / cnt1[p])-mln::math::sqr(avg1[p]));
+
+ // Assign the statistics to the second class
+ cnt2[p] = cnt2[p-1] - mln::opt::at(histo, p);;
+ sum2[p] = sum2[p-1] - p * mln::opt::at(histo, p);;
+ sqr2[p] = sqr2[p-1] - mln::math::sqr(p) * mln::opt::at(histo, p);;
+ avg2[p] = (0 == cnt2[p])? 0 : (sum2[p] / cnt2[p]);
+ var2[p] = (0 == cnt2[p])? 0 : ((sqr2[p] / cnt2[p])-mln::math::sqr(avg2[p]));
+
+ // Lets compute the invariants
+ cnt0[p] = cnt1[p] + cnt2[p];
+ sum0[p] = sum1[p] + sum2[p];
+ sqr0[p] = sqr1[p] + sqr2[p];
+ avg0[p] = (cnt1[p] * avg1[p] + cnt2[p] * avg2[p])/cnt0[p];
+ v_in[p] = (cnt1[p] * var1[p] + cnt2[p] * var2[p])/cnt0[p];
+ v_bw[p] = (cnt1[p] * mln::math::sqr(avg1[p]-avg0[p]) +
+ cnt2[p] * mln::math::sqr(avg2[p]-avg0[p]))/cnt0[p];
+ var0[p] = v_in[p] + v_bw[p];
+ }
+
+ // Find the threshold that minimizes the intra-class variance
+ min_var = cnt2[0]*var2[0];
+ threshold = 0;
+
+ for(short p = 0; p < 256; p++)
+ {
+ // Compute the intra-class variance
+ v_in[p] = cnt1[p]*var1[p] + cnt2[p]*var2[p];
+// std::cout << "var intra[" << p << "]= " << v_in[p] << std::endl;
+
+ if (min_var > v_in[p])
+ {
+ min_var = v_in[p];
+ threshold = p;
+ }
+ }
+
+ return threshold;
+}
+
+
+
+
+//============================================================================//
+// MAIN
+//============================================================================//
+
+
+
+
+
+#define LVL0_DESCR 0
+#define HUE0_DESCR 1
+#define HUE1_DESCR 2
+#define SAT0_DESCR 3
+#define SAT1_DESCR 4
+#define VAL0_DESCR 5
+#define VAL1_DESCR 6
+#define GMP0_DESCR 7
+#define GMP1_DESCR 8
+#define GMP2_DESCR 9
+#define MGK0_DESCR 9
+#define MGK1_DESCR 10
+#define MGK2_DESCR 11
+
+#define MGK_DESCR(version) (MGK0_DESCR + version)
+#define GMP_DESCR(version) (GMP0_DESCR + version)
+
+#define NB_DESCR 12
+#define NB_DATABASE 2
+#define NB_IMAGE 110
+#define NB_VERSION 3
+
+
+void init_descriptors(std::string file_name[],
+ float result[][NB_DESCR],
+ int size[])
+{
+ for (int i = 0; i < NB_IMAGE; i++)
+ {
+ file_name[i] = std::string("PGM");
+
+ for (int d = 0; d < NB_DESCR; d++)
+ result[i][d] = (i*d) % 256;
+ }
+
+ size[0] = 62;
+ size[1] = 48;
+}
+
+
+void dump_descriptors(const std::string file_name[],
+ const float result[][NB_DESCR],
+ const int size[])
+{
+ std::cout << "#!/usr/bin/gnuplot" << std::endl;
+ std::cout << "set terminal x11 persist 1" << std::endl;
+ std::cout << "plot '-' using 2 with point title 'ICDAR',\\" << std::endl;
+ std::cout << " '-' using 2 with point title 'AFP'" << std::endl;
+
+ int num = 0;
+
+ for (int db = 0; db < NB_DATABASE; db++)
+ {
+ for (int i = 0; i < size[db]; i++)
+ {
+ std::cout << result[num][LVL0_DESCR] << " ";
+ std::cout << result[num][HUE0_DESCR] << " ";
+ std::cout << result[num][HUE1_DESCR] << " ";
+ std::cout << result[num][SAT0_DESCR] << " ";
+ std::cout << result[num][SAT1_DESCR] << " ";
+ std::cout << result[num][VAL0_DESCR] << " ";
+ std::cout << result[num][VAL1_DESCR] << " ";
+ std::cout << result[num][GMP0_DESCR] << " ";
+ std::cout << result[num][GMP1_DESCR] << " ";
+ std::cout << result[num][GMP2_DESCR] << " ";
+ std::cout << result[num][MGK0_DESCR] << " ";
+ std::cout << result[num][MGK1_DESCR] << " ";
+ std::cout << result[num][MGK2_DESCR] << " ";
+ std::cout << " # " << file_name[num] << std::endl;
+ num++;
+ }
+
+ std::cout << "e" << std::endl;
+ }
+}
+
+void compute_histo(const float result[][NB_DESCR],
+ const int size[],
+ mln::image1d<float> histo[][NB_DATABASE])
+{
+ for (int i = 0; i < NB_DESCR; i++)
+ for (int db = 0; db < NB_DATABASE; db++)
+ {
+ histo[i][db].init_(mln::box1d(mln::point1d(0),mln::point1d(255)));
+
+ mln::data::fill(histo[i][db], mln::literal::zero);
+ }
+
+ short v;
+ int num = 0;
+
+ for (int db = 0; db < NB_DATABASE; db++)
+ {
+ for (int i = 0; i < size[db]; i++)
+ {
+ v = (short)mln::math::floor(result[num][VAR0_DESCR]+0.4999);
+ mln::opt::at(histo[VAR0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][LVL0_DESCR]+0.4999);
+ mln::opt::at(histo[LVL0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][HUE0_DESCR]+0.4999);
+ mln::opt::at(histo[HUE0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][HUE1_DESCR]+0.4999);
+ mln::opt::at(histo[HUE1_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][SAT0_DESCR]+0.4999);
+ mln::opt::at(histo[SAT0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][SAT1_DESCR]+0.4999);
+ mln::opt::at(histo[SAT1_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][VAL0_DESCR]+0.4999);
+ mln::opt::at(histo[VAL0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][GMP0_DESCR]+0.4999);
+ mln::opt::at(histo[GMP0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][GMP1_DESCR]+0.4999);
+ mln::opt::at(histo[GMP1_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][GMP2_DESCR]+0.4999);
+ mln::opt::at(histo[GMP2_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][MGK0_DESCR]+0.4999);
+ mln::opt::at(histo[MGK0_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][MGK1_DESCR]+0.4999);
+ mln::opt::at(histo[MGK1_DESCR][db],v)++;
+
+ v = (short)mln::math::floor(result[num][MGK2_DESCR]+0.4999);
+ mln::opt::at(histo[MGK2_DESCR][db],v)++;
+
+ num++;
+ }
+ }
+}
+
+void compute_thresholds(const mln::image1d<float> histo[][NB_DATABASE],
+ short threshold[],
+ float c00[],
+ float c10[],
+ float c01[],
+ float c11[])
+{
+ for (int i = 0; i < NB_DESCR; i++)
+ {
+ float avg0 = avg_histo(histo[i][0]);
+ float avg1 = avg_histo(histo[i][1]);
+
+ if (avg0 < avg1)
+ {
+ threshold[i] = min_error(histo[i][0], histo[i][1],
+ &c00[i], &c10[i], &c01[i], &c11[i]);
+ }
+ else
+ {
+ threshold[i] = min_error(histo[i][1], histo[i][0],
+ &c00[i], &c10[i], &c01[i], &c11[i]);
+ }
+
+ std::cerr << " i = " << i
+ << "; c00 = " << c00[i]
+ << "; c10 = " << c10[i]
+ << "; c01 = " << c01[i]
+ << "; c11 = " << c11[i]
+ << "; threshold " << threshold[i]
+ << std::endl;
+
+ }
+}
+
+
+void compute_descriptors(std::string file_name[],
+ float result[][NB_DESCR],
+ int size[])
+{
+ typedef boost::filesystem::path t_path;
+ typedef boost::filesystem::directory_iterator t_iter_path;
+ typedef mln::image1d<unsigned> t_histo;
+ typedef mln::value::rgb8 t_rgb8;
+ typedef mln::value::int_u8 t_int_u8;
+ typedef mln::image2d<t_int_u8> t_map;
+ typedef mln::image2d<t_rgb8> t_input;
+ typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_2_hue;
+ typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_2_sat;
+ typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_2_val;
+ typedef mln::fun::v2v::component<t_rgb8,0> t_rgb_2_red;
+ typedef mln::fun::v2v::component<t_rgb8,1> t_rgb_2_green;
+ typedef mln::fun::v2v::component<t_rgb8,2> t_rgb_2_blue;
+ typedef mln::accu::meta::stat::histo1d t_accu_histo;
+
+
+ t_path img_path[2] = { ICDAR_20P_INPUT_IMG_PATH, AFP_PPM_IMG_PATH};
+ t_path mgk_path[3][2] = {{ICDAR_20P_MGK30_IMG_PATH, AFP_MGK30_IMG_PATH},
+ {ICDAR_20P_MGK20_IMG_PATH, AFP_MGK20_IMG_PATH},
+ {ICDAR_20P_MGK10_IMG_PATH, AFP_MGK10_IMG_PATH}};
+ t_path gmp_path[3][2] = {{ICDAR_20P_GMP30_IMG_PATH, AFP_GMP30_IMG_PATH},
+ {ICDAR_20P_GMP20_IMG_PATH, AFP_GMP20_IMG_PATH},
+ {ICDAR_20P_GMP10_IMG_PATH, AFP_GMP10_IMG_PATH}};
+
+ int num = 0;
+ int cnt = 0;
+
+ for (int db = 0; db < NB_DATABASE; db++)
+ {
+ if (boost::filesystem::exists(img_path[db]) &&
+ boost::filesystem::is_directory(img_path[db]))
+ {
+ boost::filesystem::system_complete(img_path[db]);
+
+ const t_iter_path end_iter;
+
+ cnt = 0;
+
+ for (t_iter_path dir_iter(img_path[db]); end_iter != dir_iter; ++dir_iter)
+ {
+ t_path img_file = dir_iter->path().leaf();
+ t_path dir_file = dir_iter->path();
+ t_input img_input;
+
+ mln::io::ppm::load(img_input, dir_file.string().c_str());
+
+ t_map h_img_map = mln::data::transform(img_input, t_rgb_2_hue());
+ t_map s_img_map = mln::data::transform(img_input, t_rgb_2_sat());
+ t_map v_img_map = mln::data::transform(img_input, t_rgb_2_val());
+ t_map r_img_map = mln::data::transform(img_input, t_rgb_2_red());
+ t_map g_img_map = mln::data::transform(img_input, t_rgb_2_green());
+ t_map b_img_map = mln::data::transform(img_input, t_rgb_2_blue());
+ t_histo h_img_hst = mln::data::compute(t_accu_histo(), h_img_map);
+ t_histo s_img_hst = mln::data::compute(t_accu_histo(), s_img_map);
+ t_histo v_img_hst = mln::data::compute(t_accu_histo(), v_img_map);
+ t_histo r_img_hst = mln::data::compute(t_accu_histo(), r_img_map);
+ t_histo g_img_hst = mln::data::compute(t_accu_histo(), g_img_map);
+ t_histo b_img_hst = mln::data::compute(t_accu_histo(), b_img_map);
+
+ std::cerr << dir_iter->path() << std::endl;
+
+ file_name[num] = img_file.string();
+
+ // descriptors
+ result[num][LVL0_DESCR] = lvl0_descriptor(v_img_hst);
+ result[num][HUE0_DESCR] = hue0_descriptor(h_img_hst);
+ result[num][HUE1_DESCR] = hue1_descriptor(h_img_hst,20);
+ result[num][SAT0_DESCR] = sat0_descriptor(s_img_hst);
+ result[num][SAT1_DESCR] = sat1_descriptor(s_img_hst,50);
+ result[num][VAL0_DESCR] = val0_descriptor(v_img_hst);
+ //result[num][VAL1_DESCR] = var0_descriptor(v_img_hst, 15);
+ result[num][VAL1_DESCR] = 0;
+
+ // for gimp and magick
+ for (int v = 0; v < NB_VERSION; v++)
+ {
+ if (boost::filesystem::exists(mgk_path[v][db]) &&
+ boost::filesystem::exists(gmp_path[v][db]) &&
+ boost::filesystem::is_directory(mgk_path[v][db]) &&
+ boost::filesystem::is_directory(gmp_path[v][db]))
+ {
+ t_path mgk_file = mgk_path[v][db] / img_file;
+ t_path gmp_file = gmp_path[v][db] / img_file;
+ t_input gmp_input;
+
+ mln::io::ppm::load(gmp_input, gmp_file.string().c_str());
+
+ t_map r_gmp_map = mln::data::transform(gmp_input,t_rgb_2_red());
+ t_map g_gmp_map = mln::data::transform(gmp_input,t_rgb_2_green());
+ t_map b_gmp_map = mln::data::transform(gmp_input,t_rgb_2_blue());
+
+ result[num][GMP_DESCR(v)]= err_descriptor(r_img_map,
+ g_img_map,
+ b_img_map,
+ r_gmp_map,
+ g_gmp_map,
+ b_gmp_map);
+
+ t_input mgk_input;
+
+ mln::io::ppm::load(mgk_input, mgk_file.string().c_str());
+
+ t_map r_mgk_map = mln::data::transform(mgk_input,t_rgb_2_red());
+ t_map g_mgk_map = mln::data::transform(mgk_input,t_rgb_2_green());
+ t_map b_mgk_map = mln::data::transform(mgk_input,t_rgb_2_blue());
+
+ result[num][MGK_DESCR(v)]= err_descriptor(r_img_map,
+ g_img_map,
+ b_img_map,
+ r_mgk_map,
+ g_mgk_map,
+ b_mgk_map);
+ }
+ }
+
+ num++;
+ cnt++;
+ }
+ }
+
+ size[db] = cnt;
+ }
+}
+
+
+int main2()
+{
+ typedef mln::image1d<unsigned> t_histo;
+ typedef mln::value::rgb8 t_rgb8;
+ typedef mln::value::int_u8 t_int_u8;
+ typedef mln::image2d<t_int_u8> t_map;
+ typedef mln::image2d<t_rgb8> t_input;
+ typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_2_hue;
+ typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_2_sat;
+ typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_2_val;
+ typedef mln::fun::v2v::component<t_rgb8,0> t_rgb_2_red;
+ typedef mln::fun::v2v::component<t_rgb8,1> t_rgb_2_green;
+ typedef mln::fun::v2v::component<t_rgb8,2> t_rgb_2_blue;
+ typedef mln::accu::meta::stat::histo1d t_accu_histo;
+
+ t_input img_input;
+
+ mln::io::ppm::load(img_input, ICDAR_20P_INPUT_IMG_PATH"/mp00032c_20p.ppm");
+ //mln::io::ppm::load(img_input, AFP_PPM_IMG_PATH"/000_Del218430.ppm");
+
+
+
+ t_map h_img_map = mln::data::transform(img_input, t_rgb_2_hue());
+ t_map s_img_map = mln::data::transform(img_input, t_rgb_2_sat());
+ t_map v_img_map = mln::data::transform(img_input, t_rgb_2_val());
+ t_map r_img_map = mln::data::transform(img_input, t_rgb_2_red());
+ t_map g_img_map = mln::data::transform(img_input, t_rgb_2_green());
+ t_map b_img_map = mln::data::transform(img_input, t_rgb_2_blue());
+ t_histo h_img_hst = mln::data::compute(t_accu_histo(), h_img_map);
+ t_histo s_img_hst = mln::data::compute(t_accu_histo(), s_img_map);
+ t_histo v_img_hst = mln::data::compute(t_accu_histo(), v_img_map);
+ t_histo r_img_hst = mln::data::compute(t_accu_histo(), r_img_map);
+ t_histo g_img_hst = mln::data::compute(t_accu_histo(), g_img_map);
+ t_histo b_img_hst = mln::data::compute(t_accu_histo(), b_img_map);
+
+
+ std::cout << "sat2 : " << sat0_descriptor(s_img_hst) << std::endl;
+
+ return 0;
+}
+
+int main()
+{
+ std::string file_name[NB_IMAGE];
+ float result[NB_IMAGE][NB_DESCR];
+ int size[NB_DATABASE];
+ mln::image1d<float> histo[NB_DESCR][NB_DATABASE];
+ short threshold[NB_DESCR];
+ float c00[NB_DESCR];
+ float c10[NB_DESCR];
+ float c01[NB_DESCR];
+ float c11[NB_DESCR];
+
+ std::cerr << "DESCRIPTORS" << std::endl;
+ compute_descriptors(file_name,result,size);
+// std::cout << "DUMPING" << std::endl;
+// init_descriptors(file_name,result,size);
+ dump_descriptors(file_name,result,size);
+ std::cerr << "HISTO" << std::endl;
+ compute_histo(result,size,histo);
+ std::cerr << "THRESHOLD" << std::endl;
+ compute_thresholds(histo,threshold,c00,c10,c01,c11);
+
+ mln::io::plot::save_image_sh(histo[LVL0_DESCR][0], "lvl0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[HUE0_DESCR][0], "hue0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[HUE1_DESCR][0], "hue1_histo1.sh");
+ mln::io::plot::save_image_sh(histo[SAT0_DESCR][0], "sat0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[SAT1_DESCR][0], "sat1_histo1.sh");
+ mln::io::plot::save_image_sh(histo[VAL0_DESCR][0], "val0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[VAL1_DESCR][0], "val1_histo1.sh");
+ mln::io::plot::save_image_sh(histo[GMP0_DESCR][0], "gmp0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[GMP1_DESCR][0], "gmp1_histo1.sh");
+ mln::io::plot::save_image_sh(histo[GMP2_DESCR][0], "gmp2_histo1.sh");
+ mln::io::plot::save_image_sh(histo[MGK0_DESCR][0], "mgk0_histo1.sh");
+ mln::io::plot::save_image_sh(histo[MGK1_DESCR][0], "mgk1_histo1.sh");
+ mln::io::plot::save_image_sh(histo[MGK2_DESCR][0], "mgk2_histo1.sh");
+
+ mln::io::plot::save_image_sh(histo[LVL0_DESCR][1], "lvl0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[HUE0_DESCR][1], "hue0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[HUE1_DESCR][1], "hue1_histo2.sh");
+ mln::io::plot::save_image_sh(histo[SAT0_DESCR][1], "sat0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[SAT1_DESCR][1], "sat1_histo2.sh");
+ mln::io::plot::save_image_sh(histo[VAL0_DESCR][1], "val0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[VAL1_DESCR][1], "val1_histo2.sh");
+ mln::io::plot::save_image_sh(histo[GMP0_DESCR][1], "gmp0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[GMP1_DESCR][1], "gmp1_histo2.sh");
+ mln::io::plot::save_image_sh(histo[GMP2_DESCR][1], "gmp2_histo2.sh");
+ mln::io::plot::save_image_sh(histo[MGK0_DESCR][1], "mgk0_histo2.sh");
+ mln::io::plot::save_image_sh(histo[MGK1_DESCR][1], "mgk1_histo2.sh");
+ mln::io::plot::save_image_sh(histo[MGK2_DESCR][1], "mgk2_histo2.sh");
+
+ return 0;
+}
+
--
1.5.6.5
1
0

last-svn-commit-31-gba99cc2 Test on image database the achromatism descriptor.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* green/exp/annotating/achromatism/Makefile.am: New Makefile.
* green/exp/annotating/achromatism/achromatism.am: New source.
* green/exp/annotating/achromatism/text-color.txt: New image class.
* green/exp/annotating/achromatism/text-img.txt: New image class.
* green/exp/annotating/achromatism/text-only.txt: New image class.
---
milena/sandbox/ChangeLog | 18 +++
.../{nb_color => achromastism}/Makefile.am | 4 +-
.../exp/annotating/achromastism/achromastism.cc | 113 ++++++++++++++++++++
.../exp/annotating/achromastism/text-color.txt | 15 +++
.../green/exp/annotating/achromastism/text-img.txt | 40 +++++++
.../exp/annotating/achromastism/text-only.txt | 8 ++
6 files changed, 197 insertions(+), 1 deletions(-)
copy milena/sandbox/green/exp/annotating/{nb_color => achromastism}/Makefile.am (96%)
create mode 100644 milena/sandbox/green/exp/annotating/achromastism/achromastism.cc
create mode 100644 milena/sandbox/green/exp/annotating/achromastism/text-color.txt
create mode 100644 milena/sandbox/green/exp/annotating/achromastism/text-img.txt
create mode 100644 milena/sandbox/green/exp/annotating/achromastism/text-only.txt
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index 27c37db..d86642a 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,3 +1,21 @@
+2010-06-21 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
+ Test on image database the achromatism descriptor.
+
+ * green/exp/annotating/achromatism/Makefile.am: New Makefile.
+ * green/exp/annotating/achromatism/achromatism.am: New source.
+ * green/exp/annotating/achromatism/text-color.txt: New image class.
+ * green/exp/annotating/achromatism/text-img.txt: New image class.
+ * green/exp/annotating/achromatism/text-only.txt: New image class.
+
+2010-06-21 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
+ Turn around Millet 2008 hsv descriptors.
+
+ * green/demo/annotating/hsv: New directory.
+ * green/demo/annotating/hsv/Makefile.am: New Makefile.
+
+
2010-02-10 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
Save Theo's exhaustive demonstration results.
diff --git a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am b/milena/sandbox/green/exp/annotating/achromastism/Makefile.am
similarity index 96%
copy from milena/sandbox/green/exp/annotating/nb_color/Makefile.am
copy to milena/sandbox/green/exp/annotating/achromastism/Makefile.am
index 8e204c6..d33b94d 100644
--- a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am
+++ b/milena/sandbox/green/exp/annotating/achromastism/Makefile.am
@@ -7,7 +7,9 @@
#########
LOADLIBES= -lboost_filesystem
-INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green
+INCLUDES1= -I$(HOME)/git/olena/milena/sandbox/green
+INCLUDES2= -I$(HOME)/git/olena/milena
+INCLUDES= $(INCLUDES1) $(INCLUDES2)
#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)
diff --git a/milena/sandbox/green/exp/annotating/achromastism/achromastism.cc b/milena/sandbox/green/exp/annotating/achromastism/achromastism.cc
new file mode 100644
index 0000000..fdb8e6d
--- /dev/null
+++ b/milena/sandbox/green/exp/annotating/achromastism/achromastism.cc
@@ -0,0 +1,113 @@
+// ACHROMATISM TEST CF MILLET 2008
+
+#include <iostream>
+#include <sstream>
+#include <boost/filesystem.hpp>
+
+#include <mln/img_path.hh>
+
+#include <mln/accu/stat/histo1d.hh>
+
+#include <mln/core/image/image1d.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+
+#include <mln/data/compute.hh>
+#include <mln/data/stretch.hh>
+#include <mln/data/transform.hh>
+
+#include <mln/math/max.hh>
+#include <mln/math/min.hh>
+
+#include <mln/geom/nsites.hh>
+
+#include <mln/fun/v2v/rgb_to_achromatism_map.hh>
+
+#include <mln/io/ppm/load.hh>
+#include <mln/io/plot/save_image_sh.hh>
+
+#include <mln/value/rgb8.hh>
+
+template <typename I>
+unsigned count_histo(const mln::Image<I>& img_)
+{
+ const I& img = exact(img_);
+
+ mln_precondition(img.is_valid());
+
+ unsigned result = 0;
+
+ mln_piter(I) p(img.domain());
+
+ for_all(p)
+ result += img(p);
+
+ return result;
+}
+
+float achromatism_test(const std::string input,
+ const std::string output,
+ const unsigned threshold)
+
+{
+ typedef mln::fun::v2v::rgb_to_achromatism_map<8> t_rgb_to_achromatism_map;
+
+ mln::image2d<mln::value::rgb8> input_rgb8;
+ mln::image2d<mln::value::int_u8> map;
+ mln::image1d<unsigned> histo;
+ unsigned cnt1;
+ unsigned cnt2;
+ float prop;
+
+ mln::io::ppm::load(input_rgb8, input.c_str());
+
+ map = mln::data::transform(input_rgb8, t_rgb_to_achromatism_map());
+ histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map);
+ cnt1 = count_histo(histo | mln::box1d(mln::point1d(0),
+ mln::point1d(threshold)));
+ cnt2 = mln::geom::nsites(input_rgb8);
+ prop = ((100.0 * cnt1) / cnt2);
+
+ mln::io::plot::save_image_sh(histo, output.c_str());
+
+ return prop;
+}
+
+
+int main()
+{
+ typedef boost::filesystem::path t_path;
+ typedef boost::filesystem::directory_iterator t_iter_path;
+
+ t_path full_path[] = {t_path(ICDAR_20P_PPM_IMG_PATH)};
+
+ for (int i = 0; i < 1; ++i)
+ {
+ std::cout << "entering " << full_path[i] << std::endl;
+
+ if (boost::filesystem::exists(full_path[i]) &&
+ boost::filesystem::is_directory(full_path[i]))
+ {
+ boost::filesystem::system_complete(full_path[i]);
+ const t_iter_path end_iter;
+ float prop = 0.0;
+
+ for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter)
+ {
+ // concatenation de chaine
+ t_path directory(ANNOTATING_ACHROMATISM_RET_PATH);
+ t_path leaf = dir_iter->path().leaf();
+ t_path output = change_extension(directory / leaf, ".sh");
+
+ prop = achromatism_test(dir_iter->path().string(),
+ output.string(),
+ 11);
+
+ std::cout << output << " : " << prop << std::endl;
+ std::cerr << output << " : " << prop << std::endl;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/milena/sandbox/green/exp/annotating/achromastism/text-color.txt
new file mode 100644
index 0000000..4dfcbd3
--- /dev/null
+++ b/milena/sandbox/green/exp/annotating/achromastism/text-color.txt
@@ -0,0 +1,15 @@
+mp00262c_20p.ppm
+mp00263c_20p.ppm
+mp00319c_20p.ppm
+mp00440c_20p.ppm
+mp00608c_20p.ppm
+mp00630c_20p.ppm
+mp00631c_20p.ppm
+ta00028c_20p.ppm
+ta00037c_20p.ppm
+ta00043c_20p.ppm
+ta00046c_20p.ppm
+ta00073c_20p.ppm
+ta00081c_20p.ppm
+ta00089c_20p.ppm
+ta00090c_20p.ppm
diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/milena/sandbox/green/exp/annotating/achromastism/text-img.txt
new file mode 100644
index 0000000..4ecb7ca
--- /dev/null
+++ b/milena/sandbox/green/exp/annotating/achromastism/text-img.txt
@@ -0,0 +1,40 @@
+mp00032c_20p.ppm
+mp00042c_20p.ppm
+mp00076c_20p.ppm
+mp00082c_20p.ppm
+mp00142c_20p.ppm
+mp00215c_20p.ppm
+mp00228c_20p.ppm
+mp00234c_20p.ppm
+mp00248c_20p.ppm
+mp00252c_20p.ppm
+mp00253c_20p.ppm
+mp00255c_20p.ppm
+mp00259c_20p.ppm
+mp00271c_20p.ppm
+mp00290c_20p.ppm
+mp00293c_20p.ppm
+mp00304c_20p.ppm
+mp00307c_20p.ppm
+mp00311c_20p.ppm
+mp00376c_20p.ppm
+mp00411c_20p.ppm
+mp00419c_20p.ppm
+mp00447c_20p.ppm
+mp00498c_20p.ppm
+mp00510c_20p.ppm
+mp00550c_20p.ppm
+mp00573c_20p.ppm
+mp00589c_20p.ppm
+mp00592c_20p.ppm
+mp00597c_20p.ppm
+mp00599c_20p.ppm
+mp00600c_20p.ppm
+ta00031c_20p.ppm
+ta00034c_20p.ppm
+ta00063c_20p.ppm
+ta00065c_20p.ppm
+ta00072c_20p.ppm
+ta00081c_20p.ppm
+ta00083c_20p.ppm
+
diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/milena/sandbox/green/exp/annotating/achromastism/text-only.txt
new file mode 100644
index 0000000..0218a2a
--- /dev/null
+++ b/milena/sandbox/green/exp/annotating/achromastism/text-only.txt
@@ -0,0 +1,8 @@
+mp00329c_20p.ppm
+ta00036c_20p.ppm
+ta00039c_20p.ppm
+ta00040c_20p.ppm
+ta00049c_20p.ppm
+ta00055c_20p.ppm
+ta00057c_20p.ppm
+ta00068c_20p.ppm
--
1.5.6.5
1
0

15 Nov '10
* green/demo/annotating/hsv: New directory.
* green/demo/annotating/hsv/Makefile.am: New Makefile.
---
.../regional_maxima => annotating/hsv}/Makefile.am | 6 +-
milena/sandbox/green/demo/annotating/hsv/hsv.cc | 607 ++++++++++++++++++++
.../sandbox/green/ChangeLog | 0
3 files changed, 611 insertions(+), 2 deletions(-)
copy milena/sandbox/green/demo/{labeling/regional_maxima => annotating/hsv}/Makefile.am (94%)
create mode 100644 milena/sandbox/green/demo/annotating/hsv/hsv.cc
copy milena/doc/outputs/accu-right-instanciation.txt => scribo/sandbox/green/ChangeLog (100%)
diff --git a/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am b/milena/sandbox/green/demo/annotating/hsv/Makefile.am
similarity index 94%
copy from milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am
copy to milena/sandbox/green/demo/annotating/hsv/Makefile.am
index 1dd1cfb..a5d4fff 100644
--- a/milena/sandbox/green/demo/labeling/regional_maxima/Makefile.am
+++ b/milena/sandbox/green/demo/annotating/hsv/Makefile.am
@@ -6,8 +6,10 @@
# TOOLS #
#########
-INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green
-CXXFLAGS= -ggdb -O0 -Wall -W -pedantic -ansi -pipe $(INCLUDES)
+INCLUDES1= -I$(HOME)/git/olena/milena/sandbox/green
+INCLUDES2= -I$(HOME)/git/olena/milena
+INCLUDES= $(INCLUDES1) $(INCLUDES2)
+CXXFLAGS= -DNDEBUG -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
diff --git a/milena/sandbox/green/demo/annotating/hsv/hsv.cc b/milena/sandbox/green/demo/annotating/hsv/hsv.cc
new file mode 100644
index 0000000..a61a5de
--- /dev/null
+++ b/milena/sandbox/green/demo/annotating/hsv/hsv.cc
@@ -0,0 +1,607 @@
+// Test de l'opérateur de Millet TSVal (HSV)
+//
+// Val = max(R,G,B)
+// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B)
+// si R = max(R,G,B) alors Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]
+// si G = max(R,G,B) alors Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]
+// si B = max(R,G,B) alors Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]
+
+
+#include <iostream>
+#include <fstream>
+
+#include <mln/accu/max_site.hh>
+#include <mln/accu/math/count.hh>
+#include <mln/accu/stat/histo1d.hh>
+
+#include <mln/binarization/threshold.hh>
+
+#include <mln/core/alias/point1d.hh>
+#include <mln/core/alias/box1d.hh>
+#include <mln/core/concept/image.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+
+#include <mln/data/transform.hh>
+#include <mln/data/compute.hh>
+#include <mln/data/stretch.hh>
+
+#include <mln/debug/println.hh>
+
+#include <mln/literal/colors.hh>
+#include <mln/literal/grays.hh>
+
+#include <mln/fun/v2v/rgb_to_hsv.hh>
+#include <mln/fun/v2v/rgb_to_achromatism_map.hh>
+#include <mln/fun/v2v/achromatism.hh>
+#include <mln/fun/v2v/hue_concentration.hh>
+#include <mln/fun/p2b/component_equals.hh>
+#include <mln/fun/p2b/achromatic.hh>
+#include <mln/fun/v2v/component.hh>
+
+#include <mln/geom/nsites.hh>
+
+#include <mln/img_path.hh>
+
+#include <mln/io/plot/save_image_sh.hh>
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <mln/pw/cst.hh>
+#include <mln/pw/value.hh>
+//#include <mln/trace/quiet.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/hsv.hh>
+
+
+mln::value::rgb8 label_color(const mln::value::rgb8 rgb)
+{
+ mln::value::hsv_f hsv = mln::fun::v2v::f_rgb_to_hsv_f(rgb);
+
+ mln::value::rgb8 result;
+
+ // Is it a gray level ?
+ if (0 == hsv.sat())
+ {
+ // which result one ?
+ if (82 > hsv.sat())
+ result = mln::literal::black;
+ else if (179 > hsv.sat())
+ result= mln::literal::medium_gray;
+ else
+ result = mln::literal::white;
+ }
+ // Is it a true result color ?
+ else if (14 > hsv.hue())
+ result = mln::literal::red;
+ else if (29 > hsv.hue())
+ {
+ // Is is brown or orange ?
+ unsigned dist_orange = mln::math::abs(hsv.sat() - 184)
+ + mln::math::abs(hsv.val() - 65);
+
+ unsigned dist_brown = mln::math::abs(hsv.sat() - 255)
+ + mln::math::abs(hsv.val() - 125);
+
+ if (dist_orange < dist_brown)
+ result = mln::literal::orange;
+ else
+ result = mln::literal::brown;
+ }
+ else if (45 > hsv.hue())
+ {
+ // Is it green or yellow ?
+ if (80 > hsv.val())
+ result = mln::literal::green;
+ else
+ result = mln::literal::yellow;
+ }
+ else if (113 > hsv.hue())
+ result = mln::literal::green;
+ else if (149 > hsv.hue())
+ result = mln::literal::cyan;
+ else if (205 > hsv.hue())
+ result = mln::literal::blue;
+ else if (235 > hsv.hue())
+ result = mln::literal::violet;
+ else if (242 > hsv.hue())
+ result = mln::literal::pink;
+ else
+ result = mln::literal::red;
+
+ return result;
+}
+
+//unsigned count_histo(const mln::image1d<unsigned>& img)
+template <typename I>
+unsigned count_histo(const mln::Image<I>& img_)
+{
+ const I& img = exact(img_);
+
+ mln_precondition(img.is_valid());
+
+ unsigned result = 0;
+ mln_piter(I) p(img.domain());
+
+ for_all(p)
+ result += img(p);
+
+ return result;
+}
+
+// calcul de contribution
+float r(short p, unsigned histo_p, short x, unsigned histo_x)
+{
+ float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p));
+
+ return result;
+}
+
+template <typename I>
+unsigned peak_histo(const mln::Image<I>& histo_)
+{
+ const I& histo = exact(histo_);
+
+ mln_precondition(histo.is_valid());
+
+ // Find the peak of the histogram
+ unsigned v_max = mln::opt::at(histo, 0);
+ short p_max = 0;
+
+ mln_piter(I) p(histo.domain());
+
+ for_all(p)
+ {
+ if (v_max < histo(p))
+ {
+ v_max = histo(p);
+ p_max = p.ind();
+ }
+ }
+
+ return p_max;
+}
+
+
+// unsigned stddev_color(mln::image2d<mln::value::int_u8> input_int_u8,
+// const char *name_histo,
+// const char *name_image)
+// {
+// typedef mln::point1d t_point1d;
+// typedef mln::value::rgb8 t_rgb8;
+// typedef mln::value::int_u8 t_int_u8;
+// typedef mln::image2d<t_rgb8> t_image2d_rgb8;
+// typedef mln::image2d<t_int_u8> t_image2d_int_u8;
+// typedef mln::image1d<unsigned> t_histo1d;
+// typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8;
+// typedef mln::accu::meta::stat::histo1d t_histo1d_fun;
+// typedef mln::accu::max_site<t_histo1d> t_max_site_fun;
+
+// t_histo1d histo;
+
+// std::cout << "histo : " << name_histo << std::endl;
+// std::cout << "image : " << name_image << std::endl;
+
+// histo = mln::data::compute(t_histo1d_fun(), input_int_u8);
+
+// mln::io::pgm::save(input_int_u8, name_image);
+// mln::io::plot::save_image_sh(histo, name_histo);
+// mln::debug::println(histo);
+
+// // Find the peak of the histogram
+// unsigned v_max = mln::opt::at(histo, 0);
+// short p_max = 0;
+
+// mln_piter_(t_histo1d) p(histo.domain());
+
+// for_all(p)
+// {
+// if (v_max < histo(p))
+// {
+// v_max = histo(p);
+// p_max = p.ind();
+// }
+// }
+
+// // Compute the specific stddev
+
+// float stddev_low = 0.0;
+// float stddev_up = 0.0;
+// float stddev = 0.0;
+
+// if (250 > p_max)
+// for (short i = p_max+1; i < p_max+6; ++i)
+// stddev_up += r(p_max, mln::opt::at(histo,p_max),
+// i, mln::opt::at(histo,i));
+
+// if (5 < p_max)
+// for (short i = p_max-1; i > p_max-6; --i)
+// stddev_low += r(p_max, mln::opt::at(histo,p_max),
+// i, mln::opt::at(histo,i));
+
+// stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up :
+// (stddev_low + stddev_up)/2;
+
+// std::cout << "max_site : " << p_max << std::endl;
+// std::cout << "h(max_site) : " << v_max << std::endl;
+// std::cout << "stddev_up : " << stddev_up << std::endl;
+// std::cout << "stddev_low : " << stddev_low << std::endl;
+// std::cout << "stddev : " << stddev << std::endl;
+
+// return 0;
+// }
+
+
+// -------------------------------------
+// input image <name>.ppm
+// map <name>-<map>.pgm
+// thresholded map <name>-<map>.pbm
+// histogram <name>-<map>.sh
+// decision <name>-<map>.txt
+// -------------------------------------
+
+// Achromatism <name>-achromatism.pgm
+
+// call achromatism(input_rgb8, 7, 99.0)
+void achromatism(mln::image2d<mln::value::rgb8> input_rgb8,
+ mln::value::int_u8 threshold,
+ float percentage)
+{
+ typedef mln::fun::v2v::rgb_to_achromatism_map<8> t_rgb_to_achromatism_map;
+
+ mln::image2d<mln::value::int_u8> map;
+ mln::image2d<mln::value::int_u8> view;
+ mln::image2d<bool> mask;
+ mln::image1d<unsigned> histo;
+ unsigned cnt1;
+ unsigned cnt2;
+ float prop;
+ bool result;
+
+
+ map = mln::data::transform(input_rgb8, t_rgb_to_achromatism_map());
+ view = mln::data::stretch(mln::value::int_u8(), map);
+ mask = mln::binarization::threshold(map, threshold);
+ histo = mln::data::compute(mln::accu::meta::stat::histo1d(),
+ map | (mln::pw::value(mask) == true));
+ cnt1 = count_histo(histo);
+ cnt2 = mln::geom::nsites(input_rgb8);
+ prop = (100.0 * (cnt2 - cnt1) / cnt2);
+ result = (prop > percentage);
+
+
+ std::ofstream txt_stream("achromatism.txt");
+ txt_stream << "Achromatism" << std::endl;
+
+ txt_stream << "Nbre pixels : " << cnt2 << std::endl;
+ txt_stream << "Nbre pixels achromatiques : " << (cnt2-cnt1)<< std::endl;
+ txt_stream << "Percentage : " << prop << std::endl;
+ txt_stream << "Image achromatique : " << result << std::endl;
+ txt_stream << std::endl;
+
+ txt_stream.flush();
+ txt_stream.close();
+
+ mln::io::pgm::save(view, "achromatism.pgm");
+ mln::io::plot::save_image_sh(histo, "achromatism.sh");
+ mln::io::pbm::save(mask, "achromatism.pbm");
+}
+
+// call low_saturation(input_rgb8, achromatism_mask, 100, 95.0)
+void low_saturation(mln::image2d<mln::value::hsv_f> input_hsvf,
+ mln::image2d<bool> achromatism_mask,
+ mln::value::int_u8 threshold,
+ float percentage)
+{
+ typedef mln::value::hsv_f t_hsvf;
+ typedef mln::value::hsv_f::s_type t_sat;
+ typedef mln::fun::v2v::component<t_hsvf,1> t_component_s;
+
+ mln::image2d<t_sat> map;
+ mln::image2d<mln::value::int_u8> view;
+ mln::image2d<bool> mask;
+ mln::image1d<unsigned> histo;
+ unsigned cnt1;
+ unsigned cnt2;
+ float prop;
+ bool result;
+
+
+ map = mln::data::transform(input_hsvf, t_component_s());
+ view = mln::data::stretch(mln::value::int_u8(), map);
+// where is histo ??
+ prop = (100.0 * (cnt2 - cnt1) / cnt2);
+ result = (prop > percentage);
+
+ std::cout << "Saturation" << std::endl;
+
+ cnt1 = count_histo(histo_s | mln::box1d(mln::point1d(0),mln::point1d(100)));
+
+ cnt2= mln::geom::nsites(achromatic | (mln::pw::value(achromatic)==false));
+
+
+
+ std::ofstream txt_stream("achromatism.txt");
+ txt_stream << "Saturation" << std::endl;
+
+ txt_stream << "Nbre pixels : " << cnt2 << std::endl;
+ txt_stream << "Nbre p faiblement saturés : " << cnt1 << std::endl;
+ txt_stream << "Pourcentage : " << prop << std::endl;
+ txt_stream << "Image faiblement saturé : " << result << std::endl;
+ txt_stream << std::endl;
+
+ txt_stream.flush();
+ txt_stream.close();
+
+ mln::io::pgm::save(view, "achromatism.pgm");
+ mln::io::plot::save_image_sh(histo, "achromatism.sh");
+ mln::io::pbm::save(mask, "achromatism.pbm");
+}
+
+/*
+// COLOR
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00032c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00042c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00076c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00082c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00142c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00215c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00228c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00234c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00248c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00252c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00253c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00255c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00259c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00271c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00290c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00293c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00304c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00307c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00376c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00411c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00419c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00447c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00498c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00510c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00550c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00573c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00589c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00592c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00597c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00599c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00600c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00031c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00034c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00043c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00063c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00065c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00072c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00081c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00083c_20p.ppm");
+
+// BLACK AND WHITE
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00329c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00036c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00037c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00039c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00040c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00049c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00055c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00057c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00068c_20p.ppm");
+
+
+// A LITTLE BIT OF COLOR
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00262c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00263c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00311c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00319c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00440c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00608c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00630c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00631c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00028c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00046c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00073c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00089c_20p.ppm");
+ mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00090c_20p.ppm");
+*/
+
+// To DO mettre le seuil d'achromaticité en paramètre
+// TO DO inverser les couleurs sur les masques
+int main()
+{
+ typedef mln::value::rgb8 t_rgb8;
+ typedef mln::value::int_u8 t_int_u8;
+ typedef mln::value::hsv_f t_hsvf;
+ typedef mln::value::hsv_f::h_type t_hue;
+ typedef mln::value::hsv_f::s_type t_sat;
+ typedef mln::value::hsv_f::v_type t_val;
+ typedef mln::image2d<t_hue> t_image2d_hue;
+ typedef mln::image2d<t_sat> t_image2d_sat;
+ typedef mln::image2d<t_val> t_image2d_val;
+ typedef mln::image2d<t_hsvf> t_image2d_hsvf;
+ typedef mln::image2d<t_rgb8> t_image2d_rgb8;
+ typedef mln::image2d<float> t_image2d_float;
+ typedef mln::image1d<unsigned> t_histo1d;
+ typedef mln::image2d<t_int_u8> t_image2d_int_u8;
+ typedef mln::image2d<bool> t_mask;
+ typedef mln::fun::v2v::f_rgb_to_hsv_f_t t_rgb8_to_hsv;
+ typedef mln::accu::math::count<t_hsvf> t_count;
+ typedef mln::fun::v2v::component<t_hsvf,0> t_component_h;
+ typedef mln::fun::v2v::component<t_hsvf,1> t_component_s;
+ typedef mln::fun::v2v::component<t_hsvf,2> t_component_v;
+ typedef mln::fun::p2b::component_equals<t_image2d_hsvf,0> t_component_eq0;
+ typedef mln::fun::p2b::component_equals<t_image2d_hsvf,1> t_component_eq1;
+ typedef mln::fun::p2b::component_equals<t_image2d_hsvf,2> t_component_eq2;
+ typedef mln::fun::p2b::achromatic<t_rgb8> t_achromatic;
+
+ t_image2d_rgb8 input_rgb8;
+ t_image2d_hsvf input_hsvf;
+
+ t_mask achromatic;
+ t_mask low_saturation;
+
+ t_image2d_float achromatism1;
+ t_image2d_int_u8 achromatism2;
+ t_image2d_float hue_concentration1;
+ t_image2d_int_u8 hue_concentration2;
+
+ t_image2d_hue input_h;
+ t_image2d_hue input_h2;
+ t_image2d_sat input_s;
+ t_image2d_val input_v;
+
+ t_image2d_int_u8 input_h8;
+ t_image2d_int_u8 input_s8;
+ t_image2d_int_u8 input_v8;
+
+ t_histo1d histo_h;
+ t_histo1d histo_s;
+ t_histo1d histo_v;
+
+ unsigned cnt1;
+ unsigned cnt2;
+ float percentage;
+ bool result;
+
+
+
+ // IMAGE LOADING PHASE
+ std::cout << "Image loading phase ..." << std::endl;
+// mln::io::ppm::load(input_rgb8, ANNOTATING_1_BILL_IMG_PATH"/bill03.ppm");
+// mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00082c_20p.ppm");
+
+
+ achromatism(input_rgb8,7,99.0);
+ exit(-1);
+ // REPERAGE DES PIXELS ACHROMATICS
+ std::cout << "Init achromatic mask ..." << std::endl;
+ initialize(achromatic, input_rgb8);
+ mln::data::fill(achromatic, false);
+ mln::data::fill((achromatic | t_achromatic(input_rgb8, 0.03)).rw(), true);
+
+ mln::io::pbm::save(achromatic, "achromatic.pbm");
+
+ std::cout << "Achieve canal forking ..." << std::endl;
+ input_hsvf = mln::data::transform(input_rgb8, t_rgb8_to_hsv());
+
+ input_h = mln::data::transform(input_hsvf, t_component_h());
+ input_s = mln::data::transform(input_hsvf, t_component_s());
+ input_v = mln::data::transform(input_hsvf, t_component_v());
+
+ // quid des achromatiques ???
+ input_h8 = mln::data::stretch(t_int_u8(), input_h);
+ input_s8 = mln::data::stretch(t_int_u8(), input_s);
+ input_v8 = mln::data::stretch(t_int_u8(), input_v);
+
+ // REPERAGE DES PIXELS ACHROMATICS
+ std::cout << "Init low saturation mask ..." << std::endl;
+ initialize(low_saturation, input_s8);
+ mln::data::fill(low_saturation, false);
+ mln::data::fill((low_saturation|(mln::pw::value(input_s8) <
+ mln::pw::cst(100u))).rw(), true);
+
+ mln::io::pbm::save(low_saturation, "low_saturation.pbm");
+
+ std::cout << "Compute histograms ..." << std::endl;
+ histo_h = mln::data::compute(mln::accu::meta::stat::histo1d(),
+ input_h8|(mln::pw::value(achromatic)==false));
+
+ histo_s = mln::data::compute(mln::accu::meta::stat::histo1d(),
+ input_s8|(mln::pw::value(achromatic)==false));
+
+ histo_v = mln::data::compute(mln::accu::meta::stat::histo1d(),
+ input_v8|(mln::pw::value(achromatic)==false));
+
+
+ // etude des cartes
+
+ hue_concentration1=mln::data::transform(input_h,
+ mln::fun::v2v::hue_concentration(histo_h));
+ achromatism1=mln::data::transform(input_rgb8,mln::fun::v2v::achromatism());
+
+ hue_concentration2= mln::data::stretch(t_int_u8(), hue_concentration1);
+ achromatism2= mln::data::stretch(t_int_u8(), achromatism1);
+
+ mln::io::pgm::save(achromatism2, "achromatism_map.pgm");
+ mln::io::pgm::save(hue_concentration2, "hue_concentration_map.pgm");
+ mln::io::pgm::save(input_s8, "saturation_map.pgm");
+
+// cnt1 = mln::data::compute(t_count(),
+// (input_hsvf|t_component_eq0(input_hsvf,-1)).rw());
+
+
+ // (I) ACHROMATISME
+ std::cout << "Achromatism" << std::endl;
+ cnt1 = count_histo(histo_h);
+ cnt2 = mln::geom::nsites(input_h);
+
+ percentage = (100.0 * (cnt2 - cnt1) / cnt2);
+ result = percentage > 99.0;
+
+ std::cout << "Nbre pixels : " << cnt2 << std::endl;
+ std::cout << "Nbre pixels achromatiques : " << (cnt2-cnt1)<< std::endl;
+ std::cout << "Percentage : " << percentage << std::endl;
+ std::cout << "Image achromatique : " << result << std::endl;
+ std::cout << std::endl;
+
+ // (II) FAIBLE SATURATION
+ std::cout << "Saturation" << std::endl;
+
+ cnt1 = count_histo(histo_s | mln::box1d(mln::point1d(0),mln::point1d(100)));
+
+ cnt2= mln::geom::nsites(achromatic | (mln::pw::value(achromatic)==false));
+
+ percentage = (100.0 * cnt1 / cnt2);
+ result = percentage > 95.0;
+
+ std::cout << "Nbre pixels : " << cnt2 << std::endl;
+ std::cout << "Nbre p faiblement saturés : " << cnt1 << std::endl;
+ std::cout << "Percentage : " << percentage << std::endl;
+ std::cout << "Image faiblement saturé : " << result << std::endl;
+ std::cout << std::endl;
+
+ // (III) DOMINANCE DE LA TEINTE
+ // et peut être 50% des pixels faiblement saturées
+
+ mln::debug::println(histo_h);
+ unsigned peak = peak_histo(histo_h);
+
+ cnt1 = count_histo(histo_h | mln::box1d(mln::point1d(peak-20),
+ mln::point1d(peak+20)));
+
+ cnt2= count_histo(histo_h);
+
+ percentage = (100.0 * cnt1 / cnt2);
+ result = percentage > 95.0;
+
+ std::cout << "Position du pic : " << peak << std::endl;
+ std::cout << "Nbre pixels : " << cnt2 << std::endl;
+ std::cout << "Nbre pixels proches pic : " << cnt1 << std::endl;
+ std::cout << "Percentage : " << percentage << std::endl;
+ std::cout << "Image fortement teintée : " << result << std::endl;
+ std::cout << std::endl;
+
+
+
+ // Autre possibilité
+ // calculer le maximum de la teinte et regarder si le pourcentage pixels dont
+ // la distance est inférieure à 20 > 95%
+ // alors
+}
+
+// 3 cartes
+// 1) carte d'achromaticité d = max(|r-g|,|r-b|,|g-b|)
+// 2) carte de saturation
+// 3) carte d'éloignement par rapport au pic de teinte
+
+
+// QUELS SONT LES CHARACTERISTIQUES HSV DE LA BASE ICDAR ?
+// FAIBLE SATURATION DES IMAGES ?
+// DOMINANCE DES TEINTES ?
+// ACHROMATISME ?
diff --git a/milena/doc/outputs/accu-right-instanciation.txt b/scribo/sandbox/green/ChangeLog
similarity index 100%
copy from milena/doc/outputs/accu-right-instanciation.txt
copy to scribo/sandbox/green/ChangeLog
--
1.5.6.5
1
0

15 Nov '10
* 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
1
0

last-svn-commit-27-g18d1a42 Fix bugs in the histogram visualization tools.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* 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 097ea5c..d53313f 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,5 +1,12 @@
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
1
0

last-svn-commit-26-g3849ff5 Fix last details in the image processing chain.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* 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 b9d40cb..097ea5c 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,3 +1,13 @@
+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
1
0

last-svn-commit-25-g1dff8cf Extend the histogram visualization tools for new projection concept.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* green/mln/display/project_histo.hh (project2_histo): New functions
that keep the max of the histogram or the class associate to it while
projecting along a direction.
* green/mln/display/project_histo.hh (project3_histo): New functions
that keep the color of the class associate to the histogram maximum
while projecting along a direction.
* green/mln/display/display_histo.hh: New interface functions for
project2_histo and project3_histo.
---
milena/sandbox/ChangeLog | 13 +
milena/sandbox/green/mln/display/display_histo.hh | 50 +++
milena/sandbox/green/mln/display/project_histo.hh | 344 +++++++++++++++++++++
3 files changed, 407 insertions(+), 0 deletions(-)
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index f34508b..b9d40cb 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,5 +1,18 @@
2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+ Extend the histogram visualization tools for new projection concept.
+
+ * green/mln/display/project_histo.hh (project2_histo): New functions
+ that keep the max of the histogram or the class associate to it while
+ projecting along a direction.
+ * green/mln/display/project_histo.hh (project3_histo): New functions
+ that keep the color of the class associate to the histogram maximum
+ while projecting along a direction.
+ * green/mln/display/display_histo.hh: New interface functions for
+ project2_histo and project3_histo.
+
+2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
Build translation table between number of pixels and percentage of
pixels in image for the scribo database.
diff --git a/milena/sandbox/green/mln/display/display_histo.hh b/milena/sandbox/green/mln/display/display_histo.hh
index 1fd5da4..2ba0b61 100644
--- a/milena/sandbox/green/mln/display/display_histo.hh
+++ b/milena/sandbox/green/mln/display/display_histo.hh
@@ -33,6 +33,8 @@
# 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>
/// \file
@@ -55,6 +57,20 @@ namespace mln
image2d<value::int_u8>
display_histo3d_unsigned(const image3d<unsigned>& histo);
+ image2d<value::int_u8>
+ display2_histo3d_unsigned(const image3d<unsigned>& histo);
+
+ image2d<value::label_8>
+ display2_histo3d_unsigned(const image3d<unsigned>& histo,
+ const image3d<value::label_8>& label);
+
+ image2d<value::rgb8>
+ display3_histo3d_unsigned(const image3d<unsigned>& histo);
+
+ image2d<value::rgb8>
+ display3_histo3d_unsigned(const image3d<unsigned>& histo,
+ const image3d<value::label_8>& label);
+
#ifndef MLN_INCLUDE_ONLY
/// \brief Allow the visualization of a 3d histogram by projection.
@@ -86,6 +102,40 @@ namespace mln
return proj_int;
}
+ image2d<value::int_u8>
+ display2_histo3d_unsigned(const image3d<unsigned>& histo)
+ {
+ image2d<value::int_u8> proj = project2_histo<0>(histo);
+
+ return proj;
+ }
+
+ image2d<value::label_8>
+ display2_histo3d_unsigned(const image3d<unsigned>& histo,
+ const image3d<value::label_8>& label)
+ {
+ image2d<value::label_8> proj = project2_histo<0>(histo, label);
+
+ return proj;
+ }
+
+ image2d<value::rgb8>
+ display3_histo3d_unsigned(const image3d<unsigned>& histo)
+ {
+ image2d<value::rgb8> proj = project3_histo<0>(histo);
+
+ return proj;
+ }
+
+ image2d<value::rgb8>
+ display3_histo3d_unsigned(const image3d<unsigned>& histo,
+ const image3d<value::label_8>& label)
+ {
+ image2d<value::rgb8> proj = project3_histo<0>(histo, label);
+
+ 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 f0e6858..d842c70 100644
--- a/milena/sandbox/green/mln/display/project_histo.hh
+++ b/milena/sandbox/green/mln/display/project_histo.hh
@@ -37,6 +37,12 @@
# include <mln/accu/image/take.hh>
# include <mln/accu/image/to_result.hh>
+# include <mln/opt/at.hh>
+
+# include <mln/value/int_u8.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/value/label_8.hh>
+
/// \file
///
/// \brief Allow the visualization of 3d histogram.
@@ -54,6 +60,10 @@ namespace mln
image2d<mln_result(A)>
project_histo(const image3d<V>& histo);
+ template <typename A, unsigned direction, typename V>
+ image2d<mln_result(A)>
+ project2_histo(const image3d<V>& histo);
+
# ifndef MLN_INCLUDE_ONLY
/// \brief Allow the visualization of 3d histogram.
@@ -86,6 +96,340 @@ namespace mln
return accu::image::to_result(histo_accu);
}
+ template <unsigned direction>
+ image2d<value::int_u8>
+ project2_histo(const image3d<unsigned>& histo)
+ {
+ image2d<value::int_u8> result;
+
+ if (0 == direction) // blue
+ {
+ image2d<value::int_u8> arg_max(histo.ncols(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nslices(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nrows(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = pos;
+ }
+
+ result = arg_max;
+ }
+ else if (1 == direction) // red
+ {
+ image2d<value::int_u8> arg_max(histo.nrows(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.nslices(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.ncols(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = pos;
+ }
+
+ result = arg_max;
+ }
+ else // 2 == direction // green
+ {
+ image2d<value::int_u8> arg_max(histo.nrows(), histo.ncols());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nslices(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = pos;
+ }
+
+ result = arg_max;
+ }
+
+ return result;
+ }
+
+ template <unsigned direction>
+ image2d<value::label_8>
+ project2_histo(const image3d<unsigned>& histo,
+ const image3d<value::label_8>& label)
+ {
+ image2d<value::label_8> result;
+
+ if (0 == direction) // blue
+ {
+ image2d<value::label_8> arg_max(histo.ncols(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nslices(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nrows(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = opt::at(label,i,j,pos);
+ }
+
+ result = arg_max;
+ }
+ else if (1 == direction) // red
+ {
+ image2d<value::label_8> arg_max(histo.nrows(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.nslices(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.ncols(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = opt::at(label,pos,i,j);
+ }
+
+ result = arg_max;
+ }
+ else // 2 == direction // green
+ {
+ image2d<value::label_8> arg_max(histo.nrows(), histo.ncols());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nslices(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = opt::at(label,i,pos,j);
+ }
+
+ result = arg_max;
+ }
+
+ return result;
+ }
+
+
+ // 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)
+ {
+ image2d<value::rgb8> result;
+
+ if (0 == direction) // blue
+ {
+ image2d<value::rgb8> arg_max(histo.ncols(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nslices(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nrows(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = value::rgb8(i,j,pos);
+ }
+
+ result = arg_max;
+ }
+ else if (1 == direction) // red
+ {
+ image2d<value::rgb8> arg_max(histo.nrows(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.nslices(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.ncols(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = value::rgb8(pos,i,j);
+ }
+
+ result = arg_max;
+ }
+ else // 2 == direction // green
+ {
+ image2d<value::rgb8> arg_max(histo.nrows(), histo.ncols());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nslices(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ // FIXME ... how to fix the n of rgb
+ opt::at(arg_max,i,j) = value::rgb8(i,pos,j);
+ }
+
+ result = arg_max;
+ }
+
+ return result;
+ }
+
+ template <unsigned direction>
+ image2d<value::rgb8>
+ project3_histo(const image3d<unsigned>& histo)
+ {
+ image2d<value::rgb8> result;
+
+ if (0 == direction) // blue
+ {
+ image2d<value::rgb8> arg_max(histo.ncols(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nslices(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nrows(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = value::rgb8(i,j,pos);
+ }
+
+ result = arg_max;
+ }
+ else if (1 == direction) // red
+ {
+ image2d<value::rgb8> arg_max(histo.nrows(), histo.nslices());
+
+ for (unsigned j = 0; j < histo.nslices(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.ncols(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ opt::at(arg_max,i,j) = value::rgb8(pos,i,j);
+ }
+
+ result = arg_max;
+ }
+ else // 2 == direction // green
+ {
+ image2d<value::rgb8> arg_max(histo.nrows(), histo.ncols());
+
+ for (unsigned j = 0; j < histo.ncols(); ++j)
+ for (unsigned i = 0; i < histo.nrows(); ++i)
+ {
+ unsigned max = 0; // minimum as possible
+ signed pos = -1;
+
+ for (unsigned k = 0; k < histo.nslices(); ++k)
+ {
+ if (max <= opt::at(histo,i,j,k))
+ {
+ max = opt::at(histo,i,j,k);
+ pos = k;
+ }
+ }
+
+ // FIXME ... how to fix the n of rgb
+ opt::at(arg_max,i,j) = value::rgb8(i,pos,j);
+ }
+
+ result = arg_max;
+ }
+
+ return result;
+ }
+
# endif // ! MLN_INCLUDE_ONLY
--
1.5.6.5
1
0

last-svn-commit-24-g40063b8 Build translation table between number of pixels and percentage of pixels in image for the scribo database.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* green/demo/labeling/regional_maxima/threshold.txt: New translation
table.
---
milena/sandbox/ChangeLog | 8 ++++++++
.../demo/labeling/regional_maxima/thresholds.txt | 15 +++++++++++++++
2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index cbd9cdc..f34508b 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,5 +1,13 @@
2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+ Build translation table between number of pixels and percentage of
+ pixels in image for the scribo database.
+
+ * green/demo/labeling/regional_maxima/threshold.txt: New translation
+ table.
+
+2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
Split the regional maxima binary in small atomic binaries.
* green/tools/annotating/histo: New directory.
diff --git a/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt b/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
index ddf5ca7..58f3e6a 100644
--- a/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
+++ b/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
@@ -5,9 +5,24 @@ image = 1169 x 1567 = 1831823
-----------------------
0.05 % | 1000.00
1.00 % | 18318.23
+ 2.00 % | 36636.46
+ 3.00 % | 54954.69
+ 4.00 % | 73272.92
5.00 % | 91591.15
+ 6.00 % | 109909.38
+ 7.00 % | 128227.61
+ 8.00 % | 146545.84
+ 9.00 % | 164864.07
10.00 % | 183182.30
+ 11.00 % | 201500.53
+ 12.00 % | 219818.76
+ 13.00 % | 238136.99
+ 14.00 % | 256455.22
15.00 % | 274773.45
+ 16.00 % | 293091.68
+ 17.00 % | 311409.91
+ 18.00 % | 329728.14
+ 19.00 % | 348046.37
20.00 % | 366364.60
25.00 % | 457955.75
30.00 % | 549546.90
--
1.5.6.5
1
0

last-svn-commit-23-gfdf0f76 Split the regional maxima binary in small atomic binaries.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* green/tools/annotating/histo: New directory.
* green/tools/annotating/histo/Makefile.am: New Makefile.
* green/tools/annotating/histo/histo.cc: New source file.
* green/tools/annotating/opening: New directory.
* green/tools/annotating/opening/Makefile.am: New Makefile.
* green/tools/annotating/opening/opening.cc: New source file.
* green/tools/annotating/regmax: New directory.
* green/tools/annotating/regmax/Makefile.am: New Makefile.
* green/tools/annotating/regmax/regmax.cc: New source file.
---
milena/sandbox/ChangeLog | 14 ++
.../annotating/histo}/Makefile.am | 9 +-
.../sandbox/green/tools/annotating/histo/histo.cc | 121 ++++++++++++++++++
.../annotating/opening}/Makefile.am | 9 +-
.../green/tools/annotating/opening/opening.cc | 79 ++++++++++++
.../annotating/regmax}/Makefile.am | 9 +-
.../green/tools/annotating/regmax/regmax.cc | 133 ++++++++++++++++++++
7 files changed, 359 insertions(+), 15 deletions(-)
copy milena/sandbox/green/{exp/annotating/nb_color => tools/annotating/histo}/Makefile.am (96%)
create mode 100644 milena/sandbox/green/tools/annotating/histo/histo.cc
copy milena/sandbox/green/{exp/annotating/nb_color => tools/annotating/opening}/Makefile.am (96%)
create mode 100644 milena/sandbox/green/tools/annotating/opening/opening.cc
copy milena/sandbox/green/{exp/annotating/nb_color => tools/annotating/regmax}/Makefile.am (96%)
create mode 100644 milena/sandbox/green/tools/annotating/regmax/regmax.cc
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index 0947048..cbd9cdc 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,3 +1,17 @@
+2010-01-05 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
+ Split the regional maxima binary in small atomic binaries.
+
+ * green/tools/annotating/histo: New directory.
+ * green/tools/annotating/histo/Makefile.am: New Makefile.
+ * green/tools/annotating/histo/histo.cc: New source file.
+ * green/tools/annotating/opening: New directory.
+ * green/tools/annotating/opening/Makefile.am: New Makefile.
+ * green/tools/annotating/opening/opening.cc: New source file.
+ * green/tools/annotating/regmax: New directory.
+ * green/tools/annotating/regmax/Makefile.am: New Makefile.
+ * green/tools/annotating/regmax/regmax.cc: New source file.
+
2009-12-23 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
Write the opening volume thresholds for the scribo image mp00082c.ppm.
diff --git a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am b/milena/sandbox/green/tools/annotating/histo/Makefile.am
similarity index 96%
copy from milena/sandbox/green/exp/annotating/nb_color/Makefile.am
copy to milena/sandbox/green/tools/annotating/histo/Makefile.am
index 8e204c6..8cd7511 100644
--- a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am
+++ b/milena/sandbox/green/tools/annotating/histo/Makefile.am
@@ -6,7 +6,6 @@
# TOOLS #
#########
-LOADLIBES= -lboost_filesystem
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)
@@ -16,17 +15,17 @@ RM= rm
MKDIR= mkdir -p
CP= cp
-SOURCE_PATTERN= green/exp
-BUILD__PATTERN= green/build/exp
+SOURCE_PATTERN= green/tools
+BUILD__PATTERN= green/build/tools
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)/
+BUILD__DIR= $(PWD)
else
# Case where make is done from source directory.
-SOURCE_DIR= $(PWD)/
+SOURCE_DIR= $(PWD)
BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD))
endif
diff --git a/milena/sandbox/green/tools/annotating/histo/histo.cc b/milena/sandbox/green/tools/annotating/histo/histo.cc
new file mode 100644
index 0000000..ab0b8af
--- /dev/null
+++ b/milena/sandbox/green/tools/annotating/histo/histo.cc
@@ -0,0 +1,121 @@
+// TOOLS ==> Color histogram
+
+#include <iostream>
+
+#include <mln/accu/stat/histo3d_rgb.hh>
+
+#include <mln/core/macros.hh>
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/image3d.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+
+#include <mln/data/compute.hh>
+#include <mln/data/transform.hh>
+
+#include <mln/display/display_histo.hh>
+
+#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/ppm/save.hh>
+
+#include <mln/opt/at.hh>
+
+#include <mln/pw/value.hh>
+
+#include <mln/value/rgb8.hh>
+#include <mln/value/rgb.hh>
+
+
+template <unsigned n>
+void mk_histo(const std::string& input,
+ const std::string& output,
+ const std::string& histo,
+ const std::string& mask)
+{
+ typedef mln::value::int_u8 t_int_u8;
+ 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_rgb8> t_image2d_rgb8;
+ typedef mln::image2d<t_rgbn> t_image2d_rgbn;
+ typedef mln::image2d<bool> t_image2d_bool;
+ typedef mln::image3d<unsigned> t_histo3d;
+ typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn;
+ typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun;
+
+ // START OF IMAGE PROCESSING CHAIN
+ t_image2d_rgb8 i0_input; // input rgb8
+ 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
+
+ mln::io::ppm::load(i0_input, input.c_str());
+ i1_input = mln::data::transform(i0_input, t_rgb8_to_rgbn());
+
+ if (0 < mask.size())
+ {
+ mln::io::pbm::load(m0_input, mask.c_str());
+ h1_input = mln::data::compute(t_histo3d_fun(),
+ (i1_input | mln::pw::value(m0_input)).rw());
+ }
+ else
+ {
+ h1_input = mln::data::compute(t_histo3d_fun(), i1_input);
+ }
+ // END OF IMAGE PROCESSING CHAIN
+
+ // BEGIN DUMPING
+ p1_histo = mln::display::display_histo3d_unsigned(h1_input);
+ mln::io::dump::save(h1_input, histo.c_str());
+ mln::io::pgm::save(p1_histo, output.c_str());
+ // END DUMPING
+}
+
+
+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 << std::endl;
+}
+
+int main(int argc, char* args[])
+{
+ if (5 == argc || 6 == 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] : "");
+
+ 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;
+ default: usage(); break;
+ }
+ }
+ else
+ usage();
+
+ return 0;
+}
diff --git a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am b/milena/sandbox/green/tools/annotating/opening/Makefile.am
similarity index 96%
copy from milena/sandbox/green/exp/annotating/nb_color/Makefile.am
copy to milena/sandbox/green/tools/annotating/opening/Makefile.am
index 8e204c6..8cd7511 100644
--- a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am
+++ b/milena/sandbox/green/tools/annotating/opening/Makefile.am
@@ -6,7 +6,6 @@
# TOOLS #
#########
-LOADLIBES= -lboost_filesystem
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)
@@ -16,17 +15,17 @@ RM= rm
MKDIR= mkdir -p
CP= cp
-SOURCE_PATTERN= green/exp
-BUILD__PATTERN= green/build/exp
+SOURCE_PATTERN= green/tools
+BUILD__PATTERN= green/build/tools
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)/
+BUILD__DIR= $(PWD)
else
# Case where make is done from source directory.
-SOURCE_DIR= $(PWD)/
+SOURCE_DIR= $(PWD)
BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD))
endif
diff --git a/milena/sandbox/green/tools/annotating/opening/opening.cc b/milena/sandbox/green/tools/annotating/opening/opening.cc
new file mode 100644
index 0000000..3e1dbf2
--- /dev/null
+++ b/milena/sandbox/green/tools/annotating/opening/opening.cc
@@ -0,0 +1,79 @@
+// TOOLS ==> histogram filtering
+
+#include <iostream>
+
+#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/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <mln/morpho/opening/volume.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)
+{
+ typedef mln::value::int_u8 t_int_u8;
+ typedef mln::image2d<t_int_u8> t_image2d_int_u8;
+ 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
+
+ mln::io::dump::load(h1_input, input.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);
+ mln::io::dump::save(h2_input, opened.c_str());
+ mln::io::pgm::save(p1_histo, output.c_str());
+ // END DUMPING
+}
+
+
+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 << std::endl;
+}
+
+int main(int argc, char* args[])
+{
+ if (5 == 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);
+ }
+ else
+ usage();
+
+ return 0;
+}
diff --git a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am b/milena/sandbox/green/tools/annotating/regmax/Makefile.am
similarity index 96%
copy from milena/sandbox/green/exp/annotating/nb_color/Makefile.am
copy to milena/sandbox/green/tools/annotating/regmax/Makefile.am
index 8e204c6..8cd7511 100644
--- a/milena/sandbox/green/exp/annotating/nb_color/Makefile.am
+++ b/milena/sandbox/green/tools/annotating/regmax/Makefile.am
@@ -6,7 +6,6 @@
# TOOLS #
#########
-LOADLIBES= -lboost_filesystem
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)
@@ -16,17 +15,17 @@ RM= rm
MKDIR= mkdir -p
CP= cp
-SOURCE_PATTERN= green/exp
-BUILD__PATTERN= green/build/exp
+SOURCE_PATTERN= green/tools
+BUILD__PATTERN= green/build/tools
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)/
+BUILD__DIR= $(PWD)
else
# Case where make is done from source directory.
-SOURCE_DIR= $(PWD)/
+SOURCE_DIR= $(PWD)
BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD))
endif
diff --git a/milena/sandbox/green/tools/annotating/regmax/regmax.cc b/milena/sandbox/green/tools/annotating/regmax/regmax.cc
new file mode 100644
index 0000000..2079bc4
--- /dev/null
+++ b/milena/sandbox/green/tools/annotating/regmax/regmax.cc
@@ -0,0 +1,133 @@
+// TOOLS ==> regmax on histo
+
+#include <iostream>
+
+#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/debug/println.hh>
+#include <mln/display/display_histo.hh>
+
+#include <mln/io/dump/load.hh>
+#include <mln/io/dump/save.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/labeling/regional_maxima.hh>
+
+#include <mln/morpho/opening/volume.hh>
+
+#include <mln/value/label_8.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/value/rgb8.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 mk_regmax(const std::string& input,
+ const std::string& quant,
+ const std::string& histo,
+ const std::string& label,
+ const std::string& output)
+{
+ typedef mln::value::label_8 t_lbl8;
+ typedef mln::value::rgb8 t_rgb8;
+ typedef mln::value::rgbn t_rgbn;
+ typedef mln::value::int_u8 t_int_u8;
+ 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::image3d<t_lbl8> t_image3d_lbl8;
+ typedef mln::image2d<t_lbl8> t_image2d_lbl8;
+ typedef mln::image3d<unsigned> t_histo3d;
+ 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;
+
+ t_image2d_rgb8 i0_input; // input img
+ t_image2d_rgbn i1_input; // quant img
+ 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_image3d_lbl8 l2_histo; // label histo
+ t_mean_array m2_label; // palette
+
+ t_lbl8 n_lbl; // nb class
+
+ // 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());
+ // END LOADING
+
+ // BEGIN IMAGE PROCESSING
+ l2_histo = mln::labeling::regional_maxima(h2_input, mln::c6(), 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);
+
+// 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 << std::endl;
+}
+
+int main(int argc, char* args[])
+{
+ if (4 == argc)
+ {
+ const std::string input(args[1]);
+ const std::string output(args[2]);
+ const std::string labeled(args[3]);
+
+ mk_regmax(input, output, labeled);
+ }
+ else
+ usage();
+
+ return 0;
+}
--
1.5.6.5
1
0

last-svn-commit-22-ga3cbabf Write the opening volume thresholds for the scribo image mp00082c.ppm.
by Yann Jacquelet 15 Nov '10
by Yann Jacquelet 15 Nov '10
15 Nov '10
* green/demo/labeling/regional_maxima/thresholds.txt: New documentation.
---
milena/sandbox/ChangeLog | 6 ++++
.../demo/labeling/regional_maxima/thresholds.txt | 27 ++++++++++++++++++++
2 files changed, 33 insertions(+), 0 deletions(-)
create mode 100644 milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
diff --git a/milena/sandbox/ChangeLog b/milena/sandbox/ChangeLog
index 48be011..0947048 100644
--- a/milena/sandbox/ChangeLog
+++ b/milena/sandbox/ChangeLog
@@ -1,5 +1,11 @@
2009-12-23 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+ Write the opening volume thresholds for the scribo image mp00082c.ppm.
+
+ * green/demo/labeling/regional_maxima/thresholds.txt: New documentation.
+
+2009-12-23 Yann Jacquelet <jacquelet(a)lrde.epita.fr>
+
Experiment various quantifications on regional maxima labeling.
* green/doc/regional_maxima/cmp_quant/h0_input.pgm.gz: New histogram.
diff --git a/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt b/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
new file mode 100644
index 0000000..ddf5ca7
--- /dev/null
+++ b/milena/sandbox/green/demo/labeling/regional_maxima/thresholds.txt
@@ -0,0 +1,27 @@
+image = 1169 x 1567 = 1831823
+
+
+% image | min_volume
+-----------------------
+ 0.05 % | 1000.00
+ 1.00 % | 18318.23
+ 5.00 % | 91591.15
+ 10.00 % | 183182.30
+ 15.00 % | 274773.45
+ 20.00 % | 366364.60
+ 25.00 % | 457955.75
+ 30.00 % | 549546.90
+ 35.00 % | 641138.05
+ 40.00 % | 732729.20
+ 45.00 % | 824320.35
+ 50.00 % | 915911.50
+ 55.00 % | 1007502.65
+ 60.00 % | 1099093.80
+ 65.00 % | 1190684.95
+ 70.00 % | 1282276.10
+ 75.00 % | 1373867.25
+ 80.00 % | 1465458.40
+ 85.00 % | 1557049.55
+ 90.00 % | 1648640.70
+ 95.00 % | 1740231.85
+100.00 % | 1831823.00
--
1.5.6.5
1
0