LRE
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
March
February
January
2007
December
November
October
September
August
July
June
May
April
March
February
January
2006
December
November
October
September
August
July
June
May
April
March
February
January
2005
December
November
October
September
August
July
June
May
April
March
February
January
2004
December
November
October
September
August
July
June
May
April
March
List overview
Download
Olena-patches
----- 2025 -----
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
----- 2005 -----
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
----- 2004 -----
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
olena-patches@lrde.epita.fr
9625 discussions
Start a n
N
ew thread
last-svn-commit-27-g18d1a42 Fix bugs in the histogram visualization tools.
by Yann Jacquelet
* 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
14Â years, 2Â months
1
0
0
0
last-svn-commit-29-ga85f46a Delete BUG image file in milena/img.
by Yann Jacquelet
* 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 zcmV)EK)}BriwFqaeZols1430tUu<PzZeL+@V`*tFaA$1*r2R{8CDHQ)2<_`rSYiX4 zne%?k4k5%2Mk6$0J3=GD1`r@VKV2syGUJr}-^_1uWw+byzP`6kR%T>8{`>O$uYUe_ z|HJ?OfBS#_Km8B?-OvB!fBg9$|L6br|L6by|NMXa)c+=@|LN!FfB2vOfB)T2F8^)W z|Ltx+|Lgz$|M-9S`G5US|KI=X|LrILH|2lxls<m`J^p>{Pp<PP*YWRB@p1ioG&~9( z|GrlCzmM7eT&Df`d@cPv_7}?@?cX=Fe?O>ad$fPw)ZVXG`=R;YpY!_p+3DwJ{d<h| z?@@l9pOOE4vVVT&zmK!x@$WC^!pHINvGK9v>jNL{dmj77f8Vq!9?L$y|J!~){`=wk z-#_~Ivb>+?RQ`SZ_w3U9^?4poPV@XlPRYOc{-d1UAF%Owzo(rCe}Mg)dOm;b3kiQw z{V(im?>Bb*Ipgz+kKO<L*#GeU1^>75bN}G%&nTyXf0+D%Unk2yuK31;-yQ!bXZiJ( z|6sm8Pkhn-!TV=^v%V(rSa^{a9`9THzWQt9z5n-3tJyU$i@lYr<3ITKpXGngv}C*3 z&h=#%=aXALSDpQ3$JO{g7QQ*z=jRH>=jSo}dmsCx;~Td8FU9`hxwLsC3jX|F+5f)p z-?PmBe%+`1dm+oer&@A}$NR#LeV+c$ocPr?F3J_Z{<#?cJ(Kgr_k}ut!FM2^7tFpf z<72`L`hHKx!>;7Ve!##N243-jf4uOHWcwaI_WNu7`xRS12lUDLeZl^-a`2oN^gk>I z&sz`v({g}P@Sl}~$By}bRt}yx=Kssf!DC<e&&mOQo&T&HJUr6>NjZ4z^8U}t!DAQL ze^w5j_y_;X%fa*3ga5P~oOkCxEeBQ#{=;(coD+J=!NTv&5`f?ToN}J@CNFmO7x?jS zI<Nltl!IiCs~i-RgKRIuImM%+6uh<JJ>Cgrf#sm%W5w~WpU>ad<orR)LBhp0?IJrb zHZI8Xf;POs)34@7kGY+Nd|l>te)dls4<6R^!^4XkdY1Z7<oKsg?MQzd)~sDY`H8YH z@QnDsC;x%==W@P>2Nb`~kQ!31|0RB1mWcZ6gg<xrOY-{E>&3H+Z_0(Z^or$`nWH3} ztFq#(;GKKlvEA+2yamcn{JHP6xaXX!u;Ek>Ji2{67vzkqmsf6i>xq9e|KY-M#}7UK zsN!S6=lJ`#B%HIe{(a-W-?jXEORXGaJnFwc|N95?{lNw0AfwXdC<n{SW5-p<E3@In zD^Ui#eiSJOja|XO52<#kpU<1T`c*#9I0^$l^u0Hq8e7kc_x=F;eXG0%%|1WBe@E$D zpFf`(Tklu=)Xw+#H}oG{O2R-<f4`UarG!)bmVy57+4O%;yYC<C6k2oyr!=rc6zL$^ z`SWxB^3;Wl+JQFW{N5-D=f&;uqWX*6dGi{6_lxr|{m)Sf^iH_^NZ=F~KYVfQvz()R z{UiQIS{@$$;r<5R(0TN$mxOqR-S69v{ZFiF>8e`yjXt`7c&2uZ`Nd}6RIg6SUbt^J z@cp|kH!trDigX4gn!WdJUF_KsI)rs@U{BnvJMKFfS7MhAp<PFxqpS=F>r)1j{DUqv zBuP0a*AKF1GTw5K{vG{)CE#D*R&qjjP|xT2ea$+`r}w%Whw;27xLIAtouy$&X^<<{ zUs$iP6@RI<Bz^v2kpkVNCJ$Wp3$OgYQ~SeC$x8uCW0sOAWfL!Q)L4G;e8Y^+;5-&O zgN^clcP5W>bwB5tcpLq<Gx+!7{q$iU518<NecH#<ZYBHkxpuBnknx2gt;CiNq2t0s zuXbKTeM~LXU5@+u<L<ZQF1e&kEW3@yV356ai1xesp|n3w>FW!7`yt<uu6wNhpu74H zx#^!#B}q%kAGGDV*q>9pr6?s{Y}1dleN+7$_wo}by~^v=<YM!yly|g(TjYw)V6lkf z+$?)%&aaeDDM;4TvR`LE>!)<dMOdQ$mzz4!tsIb-cL+P~a^4K<gt6s^@SER)^zUNJ z|9+*Cnsf*#3vXdwc8<=)cdpubs^4=;;JHC183=Tgl~aCwU-J|FMD6IGwxP(t(B<>_ zd57Mj^d7!{!_QB7AOHSgCLVxtP>2=eyG*hqoB`pycuQW?9oWGs2R(*`QxX<BhGMT} zPIenTzx#WF-;Usvf{uaVEe9PPLdF1*tt1Gp@K}Ao7tZGjHsC@#9fH27MPB^GxmXX_ zyk5{aBU`$MEqFqXQ_Sul@Utxg0S{%9$z$o5KjeQ|pTabxzhCnA<9f!AP80E%Qe)bu z!rcQW`jhmz+kdChd36AH8Q3}?XidS@%3k#p>5h(?l^z}TntUej)X?q9EF5D8de|&C z`6(A-i>vx71zrvmV5t8^dC=LIfJ0?r0yK6o#U&U528MUed0))YPSuYq_Dl^0s8*N3 zY01qUCG&hF&a%7-kglLc+jZ5Fde`4~DDaE3vJ@2S2>$&#|DG=%?f21VIp7N|UBeSK zb4E!xKVV}}7zVbS2g(9VLERYNa<Qv&0TUP%!@@+Tuu%^9LMFiv?Am#vU0^9lXeHQ! zE-42i@B@Eg3%i0MFJ`+2g!`7c|7eUC&rbi`xcQH(l!qxJe!5QkV}&RE#K-ZY0A3WN z^lOaatoYosOqU;~y-D=~b+;f6I4rML_cLjq*FY!KCHE&U1??xAohChP3GBdoxC%%S zBj34=o~J(zQ_M^H`G%pP%gu}3f+ApNE}kD$JV_@Yf7TnE*B7D|-X$lDB>12IU2u87 z14H?ggSTV&_ru<*)@-VO>=NdXR0Y|(ugZWA^QWiHoV;-C9S+lhVal{IyevekdOGTN zIQaL6e=pF#=lQQISk@h!Un~Cs9cstKW#X?=Fr!$XuAs@Fz;b|6(Ci1>l#y{}d{`*J z4|ZS&Jner#ir64zl!1cDb3{3q_`wb*5_Sz3@rg39r0JcX&lM;5BIliFXa6x_Oeqb& zCyzLg`<~DP@L#`>jJxOG-&4HhXm7y{@~1c@8Cj+jtHH{)KXBmwbOhrw%Km~4brq97 zFDfwvy#1$OSCoe3h_Ust3oX}suLXz@9m147SeM;cmyP5ZUz5}gxzqWip(TJ{>p1m| zYltQL3$_v&YleS6bPt0S&#+MA@xG(88_wnv1Fz0g3io|=`s!gwS72q~xoX6Ph<9nf z+BL34YdX*jzXz&L>jwV)u5U+RrQrRV04|EhUauAL6aPXNFlJ&%sB-*S0;<7>&ECDo z(clcIY~eJYGVpc>6=eZ8`%@CC;{?D8rc4gFpB$iJN&T489q@vi{yKiTf+m-E+@o)_ zZ(iLuwr@IEf5EsVi-F`z0Xuxt-=*i?U5wB2a0<qlWg)Pxd-oHaK>pN>@3ZT^eh3Ir z$O#Ie{zfjogF_X3*6~<fEzA$zh#$1{800{9&bzi*?Ta;eyG1!*hu}9b;?uesZ@k#H z)C=tps{C1&jOU7OvT109$P`u|QY%oLMhC1|PDfxJLUUO0c>#tOS3IwaFT{AWT2T)8 zy8Jrz{Vxzk5uEiI<rLs%dwPgHFI-rx6y%MrpxGna$E$N<@9&^Z<9?@ou-F|%`W*R& z<Ik-E$^qcT$KDyB6S<;VwcsHg>|mf%K>vSk_`@-SCpL@RR96`iP!_x;<5q>O>BN5L z00}jUvA>Y*GY8Ml*^2GLqok#qoS@_U{?G!-)jd4FiwzkJUpR4J|3NGWt{>7rOT>Ov zwR_#!uQlhR%}Nv-1*OWTTiTy{Qdg`%pKkuVsLN3sdv~e~3QMZ}0Q3WD$O||bq&TF{ zAGrD7r~ZkXRF-eru{6B)>T)OafkDx7F9GxCA}ky<1ONNGSUerVKsm52!(%PNzyD>~ zQ?{<4$_=Z@>oBGNZ(0h*=jJs9DQ>^Ux0?{x&|16)>tC1j@3{gmXjT#gM*Jx<ByfO| z<?9nKUJlsM*D)2C0n0(Uhl7?`s3+bkOkTO-a>i?Mj(tWsNGJ)|f1I2BxlULH+6^C` zfuW;2z($fZMeh!5+4@|V;!?Ip`2&a5)7|}yJ-YV)s~p^aUVkj+H*e^_zaj3?QKX_{ znAhWB?YQg4mfF&P?LzH3nP|~62ZZ_gAC{DZ`GH!;r%WXNI>W((yT0~&P(Up{|KYhS z8g?Mm5q5|B9vBV@K`8<gr+n$fM&Mvd`_N^&kXcw+b>0+bqUB0ZP~^M^esDU3cbDD| zPN0j&$6ACgxFYylBq4Y?NHRPvDG37`Nxw>mpx^_ju)bp)HXADbWLHqF6uf2NJ@#iv z*ccKD%0mq((HAIzuRIq=%U{G2ua7SmJFo(lhk_E3z#^+n$^l@ZgofvR%MX+|gmchi z**M>e?S1j^2OkW$edMjgc?ZA}&iy8~+fTFzQ+7M~)9`j_x4PT~_f71%R}k|jmICQB zStvoFD^@TCoYQ@vl6&%J%~$<DmQkgAI>aH^L39ZB9wRH;aQ}i&^#Z$x1TCQiro4Bp zQX>Q<_a9Jx(`(M<U+@;6<6neLhK_m<@RGKagCeJX5i4jPFriC1&=%r@lnXrYTqJ~Z z+^4V`I3(cDdD_+dKAV{j-{5Tqo|}l!fh(?aCT#PFj_5p!0Nnw6Kk@!&**fw@Hf5Mh z*V}64fVA+_6(p2^cYCl7WN1S}!`E02mJCnx+Z7PB?KeZ&xk^(o0%8Z%3-;seF-3vT zi)f5o8yGvvK?8OGSP=}xC+LU;93*8Qrf4Bfi4YpnDF;@Eqbp!ZaFnKhz?UR<!hNCY zpBsAp+$#Hr8(KeW2|vY3Ht*7!SHbbe2NqI}b>l6g%A&c+3DsYz%h#iL$ue>ONj8qB zJU;)4o@~Ej%O8kV`7mDkA8Z+Na!x6j^1!B^pu%~|!w4%5Cn}|W3vAf(ul2%J9-MT% z1Si<KLD(PK;*@S+e#$~dk;=AL5B4&d%WsD;uxcm=SA74$Z#umUQC4HqA9XbeZ>h?K zxTPeJigrptvaX=x=pU?rxWUF&VWTXp(=ps-(zmo#_()Y)-1&nUl)Dr(M{@elpN~@x zz=vJv{|}7#@9l5P%EBoH?{&f>?nk}cEM;~KzI4nYZm{|rtd9j&n6f_By0C&cd<xi1 zW!8P$i22zX==qr!>hs+HL%;g_z#T<W;hkfU)}P<ni~c$K%4e%`-6Vf@&%ILEf^H@m z6x!!euh-{^-?e^*0xd%8^P<)Z7>D$x2;44j?@CT6b^IVtSvJ}xK~#8Cj(w9=L;Y+g z`#mf~Gn}PW821v5=((4M5v3rrwKy#TxJkZdc*}v62ZI$yP;Tau?!e)o8kp<Frw3`n zucLf*mW~yiTgE^q;P}8hD0G7i9Y!p`5{7?oye5TRL6$P014D@y*zp3=jIdRxTd+s} zhXU>%T4I;Y0=Efohk#|{=?YGppyi-^h{#Dk7Wtm;TgfLFA659``?ImtGgYPMb%?Bg znqGy>Zg}v9(uSY<=kBEIb-?Z>uL=ftFQA``QniNs@9j6uYHc~mMJwC-gI}?vB-|=Q zg$teR6xcxd@L`MG<<dohyA&hV6-;!-!Wx_FZt#|DT;B7Pm%xcxv4SOo!jzKGV^Z~P zq-qg(2UJS4FWR?kCKXQ@fg8M!{Qi)P4clrz(II5%5R9vI{=RI16oZmwZ?V7c?en62 zN40ffOR-FR3(h+b@c28;s6>Zwh6CaTC>P$&UjqVPFs-G0VM#aQfG{trk+43L7Q%M~ zwKg}D1HAQIE_Pcw8uW3k6?$xKeS44uI;^o>07>C-&7(3tR7G>KKDu1l*A2aY2wva8 zV15WcO>Viqi2oiy?dMgBG(I|nj{fB|InDpz+i1UT`e%43SL-Pxmi=?XA1a0_LKUb2 zA<m2a{@go@@}bFp$CxEAC|)z$?=)eDQOk6T)z3NE+=n(GpvzZ(Pb|YYqUwDp$>-zK z@5U(n=sF>$i2i}gwvhBE5RB=a!DF&K)QGcrEV6lgJA{cxx1nKB&`_OH|4`9jH!itB zxu_vuOy#&<uuQZK0x*J822u<MUJ5$Ct`H)i#d|;y8RxI|il=;|3@mwAIe0iJ+`U@8 zN4V2s5lTe=@8w{!6cA7BpFyFEvKjJ6at-|Z5&?(#kv6tl9Ok^Fave^Vhv#1J)=%p& z{tN72Tu|Z@7-ARvetZy%{+ZqtNhb7h$%2P;2+B-NpOO%;^b=)I=@LV5Y6UUB3xTEp zX1mE6OaDRsl!*L`B?HrgdFz-~7M@Oy1^rq=vhC;)&Y0f>&~9?I4rxoj7hsX$!JA>F z-en==f3-aMBb6T1NCF*y?{XPG@ZZx9Zc@gE;q4ao=@!nWq4Nk#ELdG)GvlMbYwc5{ zy9}AE_V0N(%R~^5-;W1EWVbK^N<3eal5pV0!ZH6_JExrNj$ICbnsfY>cJ#Iu4jlBK z-@`(W;(f4#hAmS^Iml;Nc!HMLMYK>wtRP$i9}>2Z6eeg_?u@X1?twwi(*7V`(H%U$ zyMYhZxYmcHug4_mMza4g;*ZZr^6d(m+|Y$H;hlk>rS%cGOyRLjpNpQT6*nvBrcY$y z8+=K~&NkF}=dVZktZQy;A)bl<3I$L*3%9^O&M_BviKA&cBYXQukE(P6-#)j==b2N+ zN4={v@-JaS`mpd^F5NLP6q|uF?slE*<q8Zz%fT{@(=^`?^s!aANy4r7YM=ab<)lz* zZo~E4^uHZICQblOL9HkO3*WOp<-qWT=coKNAP9kjt6k?5jH?X5IiU$VtX?FhoAFV> zJ0PPjJ1g<ANuh|Ou?q267|X71&e0V>yCCF*A;g0u7vB1bmTbo9T|;zUmpE+QbFW)f zO!*$2i-&IC@&|wXsuBAoGr7)>o#OHbh_C`<TI=_qz;c1ltp)X#vwoVG@k@JsFBhwp z$1f#Opg{*@>PN#Lql--$ju&vNmP6jivf!lj89E<OES{X$mY0wksmkt`h(WLHrdR=i zhAe#hd>?(4@VTC;Q*&Koz2F_ffNjf0hcJO5G=UUtfXIgn+HpZSD*nBJ?N4W*o;ewG znr>SGg9!(^01}Yd2~3m&{Tk&<@5Of7iS=2*v_FP~{pkkCE67R<-_lMLX)*d|zk`M^ z;nFz0{o6LZLd%0C4wQl}3g;ZU1m7eq=^8RMO>{@-QV#0<;`+2Yq$Ct&FLx=*pT(I2 zc;nI+M-MmjUOfKz#_?HC6uA#)-R7syW{K=dJ@(_KQTRY!NxQ25MJJ@I%=HHisp|%z zeFlV^4Renx`BN6Ms_<kEb}Nb@okEvIBi{1Gda_{;<pPF+gE&4+7#(%f%{=rCf~uJ> z-u;#)`Ky<_281H4C~O-u)m9+hHscx`4B;#ti689qc+45Wo`p&IR`j%97P`<C`wjRW zVU%wW297qChi`ZAmVo*alQ%wMm^h^X14Mb>9c~?qVzTSg+YezKQk_`5bFeAHOTiGt zMt3}2{w{erFife#3aKwC2OAxNDBx#8c^JVy^p6`#XSNNFFHUK_zW1dix7&%!o*?%x zP_F%UrdjKwD{Xok)lFmn@N>pz3t2uKgYxyYXt!UEh+2aA-{jYGGWH1EUJu>XhiDmC zpJl86I_Zyvq7<cH6p1FUOVP<o+|m4L>zxq2+|+{JxL}3I@{|Kw?Cl^Tcu7zT4{cQw z{R-AS1Y9(ub=x8Rvi|8D5DFd@Wnjv%Ku+~Qhp+)DHt!CeQUEB>N-Y(+k!=~4O8BH~ zmBMDS4HHVh#7A}p3CF!lXVB3dpmdy3h1*7u5#r=33OGcmsI0@mRc{j%M}gcPE!ZV1 zLF;jbEI10D>*77uFY%zgWq>{?J30em1Ee9j-N}H<;>A`jLQxza@xr<<MCrhvxc4qu zlHv5~gud2?<Wuz&=MR>q78U&u1=?@^&U3ppm0%cN1cWRLN5$mq`~Z6-R#Ih1Qjap7 zb^1p+QM6OSQ(Q`*%pcZa`;^YCs6~w*;19ktQ%XV#ZMsWO3e837D7kRp2B!8LW(e+o zSVV?1RFFGw+08{Y`qGc~2R3Zz3QiG9G8`<Ff-c=bvOgcm;{XrAI0%o`1WpaFYl89i zE-N)R^vWT-*_Q?(8zyj0@c>q^WT`j-4>nUUR+fkDW#RF4gL9II4;0zjc3CQpFgL?i z^115j1-4-I;6H%)<ciMWU<X`(FW&7lx`PbU#C#76iWv+*jc$eH-9QpxVTd7BleSBT z1RrVB$Mhh7EY$L6&(kiiA<?1hsNX-*fhL>$`Jv}!T|=ZlI7r%`gx#JK)GPS1N56A= z*NL+uI$3EQE{(B2^W<ND?mK?_LrnJ<tXO0Ls{_LI4M_k8?Fvd5Ac!yK<Gei=?JpcQ z9>y(>icK8SwvZDpfwF7|#5(QrRdUKv$dw-N2(H0k<6{lnfE$Jk>f(FzKN!M*ritaC zB%2<NyCOCO?(^4W(~vKu_|p~an`Kx^-~;azavn`|3f3JwK4Ii}O&PZow8;tHLN6M; z*p!z=SqbPx?wy)YW9ny+$n-y<887bi$0RWe4~ZpV%4t_0#LNvsx|zzZeBMI%%Aam} z`^V>=9KK~iKTjV)S@~`x`!7ohlaRE}=0IoxjszzAAqEroPZ7eVt>m~XX5iUx5q~ZD z-gWPb$G(KO<Kx$xS#!K@JpZnp_b-j{Asb46UG5V3#6YyABT_2X{0NxaM}nVe2Dm@| zd@5AvqHrd{BgTm?W?yQkTEq{f$7^w@iZm(`0&<xO8zvbFzADB2DHrW)dXVFPIfZD; zkkH~;oOg(W12KfR0mxu4`_^&GqnNv5BVI5+*ufS?cgHbz+mZ|d@(ilcIr1B&0P>|u zy}}}FxIao~`=N33p0cKyx8Gr%;uOcRjs05YM7HFI=%BE*wUABYu0lL$7#Jp4${8Dm zA*Vf&Vw67Z!nhgBTwAg2yat9YrQl<gc9X&CA2)IJrP@l`|E~SpWycs>{&Ke$b}x>9 z=yEjPbN-UppD}K`LH<j8c}^KPNB`d!X0cN6?y}yp@CtC}vcDfN_R4rhP6p~|%h&jD z8?*4L^T!WHPyCF^cCT9U&8+ixVF`S=V$o(@f!-pg56*WvsB#ewndmzazx8uJ%BLLk zKZwYbE>(5~D8^n2x_q2>H;|ij2vI`p-8q}b4qS13HjmR((0K<9i|XgyA^9B87Gw?y zQ!oQM8VxKK2cW}&?!Y>P$7)BH6+{i5QpvJes2>t?n4CR9FdkC+Qk288!z}SC3!yrD zTqaGH^2Vv|gb^D)#{e<P$7#Jl$l$U@Atd;poABGV+<W#PINPr=KY#lDF!Tx+fnOU* z<?dB+x(c3w{kruYux+?Dq{5c!D?CD7VES<fe|xoowQn!yWIv}x7(lLG!W)nM+ozsC zcl+9_pn2VU-V5)P3h>M`v4TYeZ-l_(^72!jMgOH@d~V<+f85lyD~JV1FcSCIBwspa z?+!K&8!tqDPRe5DDXt$Eqj?3d#NRa`9S<oRjuo&A*fJ34P>}H`@-g9&!Crpf!sCj- z&0~#$p+MO1E)NF;?o>VRsv86xe?QGDN&<%kryUJ<II<yOV?a1~0fq#Y1pc6+)=?q@ z6{9m#CKh%|7~jrhx`jBpZ?2O6uqhkKirLOt$`}TYx;f!N5yJ@9p|cGM6OyUyqyw== za;#A|3)NBfC$^3ABM_^y&f9-D*j`>A=?}v3__P*Z)9ZQx5s#ID689V4upDz`R{#vV zqE~xof>&bS3kQVvs3#o#fzIHq6&2sS<?pw33UBFH=J@+q>wxPZsqnUZRxw3x`03J9 zKEGNjVM2KhfR_T@DirSwp5P%BF;bNv2_CePJ^<eEHePs2MTUg$oxe&MsPaNL+|Wz; zbOJ0q8PB51=P8B*e^v>;pnQ65!xf=`n>0`k3QEGehOp@(AlC(mtZbne)q9)Z1mxmJ zKeh^kuwjptg0CHF{9}}d+njb^u11gD?SanZ4jpv|%9(-eO!1uB!a<mrGStDUpxH}6 z_D(r?2ZbIg=WpP`aIe&MlMghY<#JY>!tE-bMf|09-XHUW_SyEg&oVxLHwf)BWQ#@a zrHz#MAu65E<xLnY*Py~zZnH6kDkJ99+X=DQ0S&U%1`Z3a;WxX1w~aVh!p7eXbP=!C zcAgI5IbVE%$p!h%G?$vN=4jz7^<T_JPr#%3p1v+__4@2PvM>=(p++H^;s`Kf=^ny` zCfGrf12}OnE}GHw#R-Ht<Q68hUo#!T6ZpW8j-W_sh`WD)n^}g0w@~{x@o7(caW{RJ zhYbtt5Z>kEF-EZMC6<W`&3}^ZK?+ABmmu|k3lB~(yc|3i<_IOT)sEyFN7j>DsfdfM zZP6gGGf3dn`>ldeIYsQ0voI7l>IuwH+&Ljn85Rc0!TT716j2gRIiSg#uNvCDZgA>R zl|>_OE7iBxkDFOLp4dOG67%Obv^ahgT6-?BzONU`A3va8n|<#jQx^yIjH{Hi8t&0? zRp!gADv8pj^4;m;RJ-EX9~iNqQz+IEbX<(5T+CzFVR1t5)bKuD<DqvbTJQty3q?E0 zie~+iS3lR{<7)%#z~vGye)*VPXL_aP&@Rm)o<WEVO4#bBE%)u|_wC191Q#8*&)r`@ zaGxxa=LFghxiEquc)*Z>06BRX@i6q4ic;{IA`+th-uvz%d%4NL@OB6dC83=c-=Ngm zq_<x(DD3D4942hCQ1nuu&gl!^++?}9!t@}EB$P_U{x&&nO88ZV`>`ZxEg(BCIHfyU z0$HKZ|6~c4NLu0wXZ2GKUeEV}au6Y|lHOSE&R_`F;Cx<hPZSr5E&87{VZL;^S&cu| z;q%%=iZ;J~*teqOr3dD=l5pO74GPN@21JYBz`Na!Hj~p>MoD0w%j_yhL?0i=ygR6Y z`W`s3+kSY!lk5HSgc0~F$$!h^sSEFrum~t*lmb4weXU^JpC9p5@Cu3mlHWC^QX{HN zAW2Q~*R?genmj`6n_iC;3oV)IPbuiPY@kpMEoSINrmqVQ*qkz&{PgkZ4zhgAGAQ6# z;qlm0%y-M+gLSc@eL94RlCWc7cuT@*aux*pjt#UA#fV-7T^5L4h{y*km_b6|3c@9g zIR=KSJLuOY;qjalPiD+QDi$*3CPBMNIcRW2V~cRvSAzd}7LB-8-V;r;gND-3jhgiQ z+BUkjiJe0Kh#AVq^k6dTqD9n&rr>yKXaOzyW`77z>DL;fN#>n3!KVx8grS$%XC>to zsK5u;2-{6A($er6>^N<q3eT;<YOuo&J!ILjKP94IcxZO~xp7z~wtG%(SePEpuu!c# zIFE!*0SuY<^G?T_?Dhmt9Pe?zXx<4(iSXJhu3+{<lK7qsfrOSAgk+bHM@v4u96WIo zFP}YN!wbkVkUt&5@EiXV13~+Ml}q45HhM)GzU7}oQjrBnkO?Be7We(%2`f5=kkK8? zm$F#SD}T<z&z5p9Wl+e16)YJJtO1Y#Va3s}EyN00L<||)rqaaO=zUFk7S0h78_dY3 z6yULNHRmbaL8ZL0c#9B31WFpIaOQ;t`0$N^aH4XDkl{=W+&ZEQxYW->^i5h8oW%0M z6WxqT*vii58*(#KB<E^`)9rD&?BY`walaFIB3t8dT`uvYsZ%$u6SZb<-a#3eC1G)< zD&59#Y?xAY#0$Uoo`WR-Bj{K%PFp}e1{!qK3fOU+PGO)cXebZMhKDmipllRJw`4K6 z#+@bdtIjF`thrR_@w>lR)&wNfWV_IulIOQrIITbTilNKh{zDJ&X)KG^;buMI4SdVi zpx?42U#Kah7?0;(Ewp<d>Ky~{Nrx3-f^fQnm#0+Bj5*WnoXSMrz`eIG;bP<j<V9to z@Cow76j5TSc=47{U#j7iTq-`^Vzw3@shI;L!qKQQT2Pr~Nya%F1h-2Q%%G)EMevrH za9bH*Bc5_#WaY<f(!f~uGEgJq*}H;yZz7$F<cSO_63ZWoVvRrV@Zr*-;kmKJ@tK8@ zjo7u5Za4KevU<x$J%kdFu}k+P_7>NS--8C%-?-e_W;JmQ?AYmWI5<=!EFVt=c+bu$ z1Kr92zGy=N_Fxlm;+p6b@cFv~Ii14UWq>z7`<)}=>)iH<8mb>{LREvrEg#cuZ9@|O zs-Hz;M=5Z;b^NhrXg5!Z?S%RqmL#W`?}iQmUU&pE@a`e|yZ1Pxk(7({MLgLt0O%YI zTZVoHg~uj^a^9hzcfF7~EEMSu#O~A~!|*zMoRdUiLP?)BL-}kc3;uDn&4P6WC0b2( z24G;G%X)$m@SfUX@Q*=c4AI`-QV`CGVRuUqw?#RaSTG(a@ojP(Pekg%(KIbdV9+E6 z=`87vW`?U-)DC(hQ%zG2<b3yEFk&$4*0@&?EB3<Y-un&%9sh|B6hr8~Yu<dEHNy|o z2ZSs+2urT54vd<>LMdX)k%I#|S_}*aK7=vPw0P0yeBlfNDY!}O3|_2prN9e(SeV!2 zv2Q?NX{JHd{$vy<;e?iu;h;r(+}74@kGgaSN<>e=g4iv#+t%Cf*dXLB$6hIm-yVO6 z*n#8(4he(?uf<{iNR#gE0bs-Y2LVC<kv^LlKeQqT+HBZO=#(kUy(j@&7K`d$MA}hS zUcr<VL~)B`=Nnvpsq^RhH$M}w;kmb<RY2UkQCd;$-$VHptcC;X-9ZLn!*Rs!xl6HP zP(y2L%ZzK$&>g%m;(GvJ-|4o4k2KP!E0{5^oDv|!1TYhy01?zXX$Q7=DMR}0ixTv_ zEdWCOl!NJ~`n*P~{EF<h0l_tr<nx6hF|2RV0DCM7Qj%~lvd~h$=ZJcytPqDW3-6GU zGZu{1OM+Q?p6y~nXTS$b0KdM`5p;Z9whDhMo8gViT7~n)4Pq^I=u?;<RRRUJ*W%}X zCx(1p#ONU3nHh`Wm4gRx1{vep4(Q;}ymt6R?+t+a73h&K@r~XAXls#W;h41)B>9W) zppdRTXz;1=co%1{tnsW9cquzWh&*DwndgW~@f-URijfN!_>EJThk_@#m;sq9KkpCu zA>S2PV%-5`C(Ot1yVB6VylwnI^S3vKCUP5H|JZ&mqie?w;2V=x*dd*4`H52S3bQ$m zbcVM!iCxO#uAZes*eC_(FQ?pVs@$lWxN7K4tIMO&vu=vJqI3zaZU&|@SNy;k#wx;l zOkx#QM9`3ARW;rFGD^V#rie!1isUzV0ubG5Ks@fyC=6^H4p#8C2%T)@9+bs%+(AG% zpAUSVWjGk<3LICQC<z@8V>g&Mb-21LN<#Bf|0v(+&Ta1Eb4iP=9wbwcq0W}p4%q(r zWjR1A)4T=C9|7Q{0kCH356QArEPr$W+8yM-x`G_s0-q$t2k{&?5aDE}gR`Ls^FB)& zfEu9tm|s7-gwC!&HUtMJ$bvC;u|SG^6}$C9-0=1%|Ni^aB3!BK6<n>{;s$=~2)g}% z4zb~4v|lD;u8T=ZzoA4YIZ+eFkj94IzJYk~mV?tBRADMtkv8In@3;ZGf`+c(eDnSY zo-~A|-u8Sx*3y3A+C(-1682^D`ThzvKM#o@?iA#H|2G8{mSh`~&s9HH@5U9&FpYCz zrJ!_@LX&r_Yz|!!2G^qqWpR>!gpzQE0sso<hTmg|IH#R-MH?8N01`T|#cXBZ?cdMC zk_ykS=3>+$Dcptooh<LhkXE8er%+>9;1mKy;}c?zDHI}Z3X*IK$eFDYjk#mLV+c)_ zf3)I7z7_-HGy4B31^LqrG(j+y;0XKfV@y&3U0;TL&%@D@^!8cGK=%c}hgX2%AOlFK z#0uCx<d`ttN5c)i+63~vt-(Pj#zuEABq_;mSgt>QJA!jyi<E=G)L`1m%}?S&x%fyi zEF_6=9<VL}aA5yKl5$Y5BOgxdzXpVU8>uSAl^P~DnMpD<R0qAEyH!}%_8`gvOuonb z)R-$|u>am+xNsp$qE<(zP-DSp7<KYkt!Q>R-s1DZth^Gm@bga6)cP=!IXM6;7+@zm z-yq@h5n_e|9Tvcb>;~2l5Mev6X#ZfF7wDWWNjWtZz1<T;^Cjb3lLv=`CY7FxqmCDL zw@^0w{k4fNfr#r0#($5vBXwkj8NT1te{~4yE(JWn=;fYUl%VZIzuC;C2n!;`Gc(iz z{J!^~^&Xgj$Oo=m1Q#q9rwcq6qcF2Oz`DT|%_%mK#{gj<&AqY|O#8<j%}Z+^Yg6b_ z-hzD&CiZ{t59tM*Vc#?=pFT?|X#YibU}e;px(gXhEH<Yk&<7<4bm-J0!ia$#Jhv;t zlI}pgy64AbJq3PXzU5uog!<W&Pa!10h$jWfi8-9lMXp`>c3g4(;}+wC7StNx2v4jW zgz>_*A;T$;;iWFWK{no7%wH2)!xd|68&78d_ThVK$C4odxB*GZ*eFOTz*NCi!^9e5 zg^)kozt%y@GkC?1T|jiY9w^c+K6mrGeB0uBchG6_HbcMU1Y?yW^-FWW?usO-CkWS} z&I|=!5`l7yQ|(_LECuO9KG1c8YMZ9~c?u!QOTmVGvCHZ|iBfR|l;|D7B}Z!Eed2h* zmO-Hjiy2Jj3W84^C<Uj6v~$Nl_aFB<RMT_H39$R?qCY-k{%D>V6Fel{C=AJRPCs|} z!XQM3)zh&<0&ehb6{vFVMSA~ocL?YvAD<vDm2(zq;`;RR?S>X@K=>wWkJ~>;&91lM z*j_%+^FFa8>te64<bO`$+rB*j%Amx=a2C$DACXw<H@bDr%qR<WlUoj|;^_=FaDs$o zqBS;7LE`7^4vv{uGE$u3iY5MnTga0{h`eIY7D2v<(`imUNdXo2E~3*OaXLyF@vXx4 zyTIdIq)qRWaiuM|shLZV$}gRqy~R#!;`mX8T)bsH(LM{s!TnAVoZw4Qte+Awq%3Sl zJDzyD=p5@3zXD1Kau}iK@5vr?F&Z);=!!vUiq-$o86eJS?%*l0@2H^96gZ^2ty~7^ z4}K^ygAUB#okopBY@M^Q#6b9RRI7@?Y7;1Nd3W#_u?aGxlh9bLJ9u9k?+!G?Rl9;p zgGAtrn$mdbn1+B=Q&f7%*)z{gq#btx!<rvz&2~?FAA`-cbq0gZ-FvEdd(7KhR16ZC z;`q=3F9--mWTM{7>ISaRiofTP0N2YdwgVeNw{*4zE7;1bL`iT85;qDJA%lmI0AA8r zMGRYry*&GuHj|h-mZdCgDG4uHUd&4Ty%XA7T0Vkx?uF;4BbdHGLMopIx0WzU^v+1L z6Kn7akP~kE;@M<tO+=!*L9-jm_#RM&$`@s^eB_KpI)NcIq<y$d9gsUxTh5@^XgR<x zpvnf}%P+{>AdCPHyc}%NBM8>WW#jwx22;`#VL*Lk%g&?=B1?BK1G{nIkT&Kf8fNhL zABNG(tQ<&pKs&W7#Fgcs(X)<42i%kQT{`GQ5$vFSOb^69*}@crgLDP76nRMY$}fAQ zq>*ha7<0eu2PNdy{S5Ml_vi(uic0{h-IdJnu?2Epi@lttXcHLonO2Osmz*yIQFbH> zD2ok>)Q)36fh$73Xahqw++ZRNh<pPhCrkkwHsXXD$^awF8%jg9?%>1?btBxqr2R8B z9GYD@IOF;K8$2;bzM(=d;!-AT2FwB;Y{y=Mcw8hAX?z6A1qYA<+$oNn#Hwc%r-}53 znAsYE=e9`JO+xu7o9Bnp*p)}U!cO76{D+DOr*#BboJo9>aIp=WFbf4_@GU|UNA>2e z=?)#f8iX_RAH31dgN^$m@)ODCPD>m~sz?Y5a}a7YeZPi<BFT+IwhPs#k;j9q(hhWj z2*oJ}RiMQ2VI<n0pMVUjh+d|USF+?hFIjt65zsm5pva&w{|GtKY&-@-7nXt|`wJ!J z_vjGZj$M2~R&zsuC*1SP%|ptxMOwk|NOHlY?ou3kVF~|qYM451@^DI<MUFm~iV3pE zs?G@u5}SrQe~}W9r6fR^_db(trQ(U{0nT5=$WK9OZPN5*QK4}TY*erkH{ztlEx``| zt)eW$A{|1)4LwaM2NNcu*s}S^yP^$qVU%M9RlcA@0@#I)^7Id;5OitLT-%&;u9vcf zYE<x07+}hyQI1qaa<rvhK+EK7i5%W1wK<mu+k8yq6Cjl;?P^ptJD`{@#D2hH9S$XN zd8TS~2Uz(!@1RO3OLmFl6Do)j-NC9-e430U3&R363r#R;6)VsS#k&NdCpzD4T&%?B zrYDw*bP#^SpLhe33+LcS3kt;Lu!3(`^TTsYcYw5$onWUxYGMH1BX(hAyYtRiN+9fK zhfY3H5r4}UI~brHy<~(*f0Pxl;VdqEcuq&K>W?vEDh2`jv+)aN{M~zY_QKyS?;ale z1HC;;IDf%Y*sh^2o|xssEZZyv(6J5rpm31Fq<(oBl#hVfU5w&IJ?~QQLC-6=)6EVO z+7i|sK>nT^8)&k=+@Is0Kixu18d$_9uT-b~t`saMbOf+8(Jg|X^2t)LWI!lFI^LpE zOtD;Sg2`Daz;MtFy|HPNs~NGC%T;IqXxC3Yp;cq22zjGB*eC~NDOZs+elUYm3L5(m zE`hRO2Q$invl5edpfm=#0zkOJTFb}yV2NG5KDIDn{Parr)deA{Zvn<U-yk%iTSLl0 zl|H0@G7!jEZkL7!s0AT10twtUroa&J?nIf`BJg*&gdU3__qd#5eqg>^HZX9gSWvK@ zp#VO9#05@AkWLvej)A%O*q^tTiDT&XZu6~H(EY38hb-9qSjX_*GVJ2wtz`<~v~pj} z%pI6zPH@RLBFpm!Lj9D5C+J03<87e81CD_{P<J8NW>Jew)|2r$^5egdHP+}5yjv*J zT5yI*7`90`{u-Q}XGMdvr6Ogn7D}uiD$~}Qm?c7=aJ*8NQ;qBt)2n=?Xf5fWk;QV( zT};`_C3Xcv+=&_<IDjjQ!kJq9DFZ{HrYQ;eAjz<RQ_9T(SyHZW-fDWlt>cGy;BrQt zK#Ndb!+`UNiJ{8$pw>v!FT5+GX)TpYCn+#p?0jaxggSxv4`WLrCMq7Hv8LDrvv*Cd z1iwrWptpVQVHt5kL}}jMY_#w_8N>=wjCJpX&h&9R;stm9fd`YxGw4Sf146>TS?7D6 zs2dwKWdU0(ST&d#U?v5?K`9KMCT1`9<j{9i|CK#-!u1=M?di~<v?3J646`?MkLSKz z!!xuk`42jB-&{Mre~Xlz$*i`GNj8*Ou!3tyfL#ZvM%_kvGJ)(gugkvV3FjgWRMfx( zWf}_O@c5;g!pO#a7T<}f)U{lcCxHl8=vV_UbKoqGkM2DHNh=R%Au#`js-}n?yQep% z4-ct2OW6(@Hwz7tM*b(=^$L$eg<c<)w3j-0_74RK!-SdbY+(ld#ZZ<p|0`L-;^2w# zV}2ro<_$c26cE8=ZZr&uEJWKPfSQ=K6&0OAPM2u+`R(a1rmJUISl}ICK<FvlZ2H79 z(T{z`X(ron`4+*BeV6YOa`H>;u|F)}jNQtBZXb`;PeZ7H>8#H?)wsD{6tj;Q5IwuM zUFE=8iH;LY?+~7^8+zyvYWQx|9w8+K7aWZ@k1oRtahJ_w{)3~Gr5xPcgD2Jv)1T&f z29yW*jXd9O$ZmMnK`|=IE=&OvRx~#b8>;^anxMBWH~0@Hcw#cmL)t5>XflH;q5d7y z+jM((2<jgSz`)$cg%M8j-qIZ`Np$SYbj}x+=o+Ss4tnBbcQ6w99{`|uZc_s>Ik;9* z^UiU;{8JlE53(qon+y!?Qx?=bu}W|6cAiUUh(mtZ#}{-0sPciLQ5ZEO9<`t()SF+B zs`z^}NgtyHUm_c7i6W-TP6wpq267mKNZ#S$^)}?)-LZkgNbb_>vrE|M3~IQh9nHdf zb~dMqEn7$8j0qp5OF-VpdsjGby93>%o%v+i<Gw?ev|S_8y{H{(1Kszk5y25={@=N% z>J;<QY-egD?28vX>pVDK!Gt2b&?=&`a!Ht;d$oYKEkTu+`M>MHpxy;THV9QCnOCTt ztB{QyPHf?9bf%5>*Qs0zg8sc%n3ok9VVp>4lvbPGCrd#UD&mUFM9)DOP&g;i>|<*Q zx5dXTIlL5zY`PGT$wA~f!$SUv^~FZK`xr4KxF(^0x`QPfg*%Jo3G2NjM8Pil`!9Yl zJwTj9<tJ(h<TaigwtntyCDahEnT$2XqYjfcst$=3$_^XayH;gd?ZRhE>*M3!qS`Y? z=m=DobqVwvZrCimM8VJ+sP3Qz7O*x-K?zyoV(F;%Lkvh54lG#BfPmrD8Ll9}Rq~F( zXpAQ&=DZdI8zrA0Qqa&kX24ZK+~Mk@D>Jc+j%f(-7-EHm)n3Ia&vS|jE_>cb$Y4(D zjy9v4<~D(@HL_`VV#Tn7DVA~9^IXJ)rD7MsG6#i5UIC~>PETy%UPR|~&so{=s0Cd? zwV!rs?4*xjyAv%vDg~iw5Fpx#_fpVMoVRobLKN&p+|jDspElDQk+$F#>-)n}j-z0k z1(3G6Xgt4;uN8F5I0Y`OQYyqL%`KLh-uSiLQ$ue?g1SZ_mI79+5^aAZw8&Wu<0FI` z@%LA#`c$&h7D>&->oDTtOSk|p=+G^KQ)p&Wi<;qcG6<#}JW#I42dGHySFj;PTDmb1 z3F97+G$LVmS-CI_;rx(|znEShI6*{rP*4gw9s?VPYG#0kn2^lN-MT}F<h&%%{${w7 zgl}6iBbIzPSRe914|}<UUCv20oYJ0Kw{13J4fmSWP^={^`tnaLVqi5LH+U{NBdWLA zWPyM0`hz0qMTuRY!G`<h5YQ`?@<tMnuZ7~0?ZW#*n7h=lzu8$PtP+3=y=C1l*(d_< zi~cx~v;*eoGkQ1X9-QxC4~KHVIq5_!xi@<eUuCsJs-7-p_unGksTI&i)BiaKHsG+K zNR|BSJuri@WDn?s!6`1`<&OMtceDynkGU!rsi{jjSRWf8$v2zpGGKio7qJ66oL&wL zi%k+uoTU^L$FiTW+S{RRN~*%c{<xt-NT4oGDvq*ohTSLiz<}^KaV&uH#u>xGrd`q! zrL%($!y!cbfeW>zVB!Ej3FHJ06_Hd}jK}ys?ejKP%IHV<UX1Qx3tk0{?mIrhje1WJ zHM<(bE;LAHT^TSy+9#2x18`a_(i)l|+$1jrRSgnlzT@bY0Ee71kY%AbK6-?SQs9=z zTp~&&a}}cl;*O_8U=>kMR{(^fV!U5Pt`GI&rb3=&V7fKIW5->N{?I+2(g2!-t%Gxq z5^9lc^hkBHM5QH;;2q=?-ND3vXjVvidmt?_NhiRVw(&cSZERIKZ&AvOkAA8D5dlKJ z!A9B|4GboK*oYRxKdvAzIJ2R-*OHV1YLyVoxy3y%Ez#)X=9Y%HrjPUL8EcX19}`7F zYXPBugZbGSv@Thg`%xdrP+sGsU0Q+_uoP?#2+7S3j$H><5_cKU6-4pPrL`e%SCD2< zn{$r9k;16q3jGT41N3*KYMH}E5}`u-=$Dw%n0)%W$^o_tkKM3E6WDU_Ur#wmk*wyP z1yyX5y=PPXV|F0(Tn{|arwsIuQBlDu_C;ci?%<*t$uK}jvDPc0J*<!_fS3h7o7+XL zC<9DxZ!MP6#}&;-_Wl8Ng(||Y^%{cq)w~=S&|Opb_W7`o#0sfPIneH)11mVUg@*m; z5Gy2IL`#r@FR7zEJhvjIinE4#s5Zd~ME$&NIbs63nT1Hpk}!WRI^*n89yF+gDdcW7 z-qCZ+;N2%N0nd_e<?+Oo_ni?gfqo^nSaG{~3L58*+?8{Z3vcLX{9lS9y;0rppheJv zTPedc_1yeVSCAYNprYAIKz2S7x0QiO=8MGe_{4BZ!bVBxh$O=*ra*kl3v+<+0qxi0 zR)fw((p5vb%^=QemfTt6)R;3Mc(z#$CQyN8=P0gUiC(SAq&86slFWCW7ishGxv#V& z)~DmPGywwm+>#?s4oy4?Cdz==BsqLIPb2VLW{8!HLy4Sn)pr#?!zrcUxT2Z9z%YYy z-;e$CCLIEBgqeM_0Ca0fIarbn&(y{$h6kL$vhOsW2wtI|a{bsSC($u+)1WwlvzI^l z@0YX2xI?nt;M;nC^DS=x6<h^TMGH4SOU7|If3fB~ku#dFoF;ml*pP6yp*G?vMqQKx zTU`Pjo60Ex>aeXi;^hfO;^kMc;>cghW|RQ(lO`mNyZD0f;qm75V8@>mNB3=H3<v4z z7ElTjYQ(;Y37Pid#;HDK;Pku9;Q`5XL+2Hy2lW%6Fg_Zl<(~GZCLiCe2E!0H7v1;G zyTZD2pqTs6kYmqdrJzTaisJ?=_UJ?1jHb|yxM=L`3h0Jj#k;UbREjbZrEI{~<Oxb5 zNZ12tD7h%5-t-7qo7z2la1S^gK(IT%fOp;%$kvkE1sEPcdeg%fwx*vOJ5avCjuL@# zV1RF7Zx1_#)+C2XMi(a_f0vBDk<_r~^(hBSq>NQ===DeW2+!HjR|;>l*sRH5;I2MH zI0vzGd@f+p^Ul$;?utR6M}AeRxag(}o1efHBm64z@VY0#S`F!-8Ugh?o`?-;Ym<XP z3FosVmdTVm@_QFC1yGCvBUm{086t&tx&&?zqC3bkL~N}m-FFveC!SFfmX!oXk|mhp z9TgsykCT=Z&f=b8deB7DSc0Mm%M!cLDt)(bU%d!{O#pw08ag%&co$>&SlAWxyGQdz z4;NeMe=rZXpLX#!EYcm!AR>DzdlFV+3mX%|9@PsKx6j=r>Z3(hkan<FJrbp=<FVBS z7Y*|6@6h3rziYA%?tt9)5KGS;U4WY;GQ73NhQYPyT=kAESYi(<w3{U+85sI~>OIKS zH;zk2{VCztV&3Z;hU5c<wdKo}%<;c0v03Y%aOfBete!BA+5J+fde|Ox9E-97Y0``Y zcU)gm3aYzG8c|`O@y*`k7_A7DqY1I{;T251<1@P%o~WlpG*dvI@<79bExZD@7QRTF zfj?q{5L<;Q50r}YeTTA`rB0CGL`ifCz0=7cvVP>ee&=S#ofp~z>yWm@)>SD1U7$rA z12^fd^xYb=^V6Glk&*zuQpelBaMIt_!sO$)+ffVYrhoX~>bfmVJTWP8%tz9?6OWaB zI0O?=et~oa1~||^2|vAMxbtY-lo-Sp4M5tG!Rik~`TW40E<pLgNd|@GYG)&q&M9_C zv1yE)6-{FkJ7uUO9*1}iH@ct{RMEW&Uck|%i$kaJ3hGC!ki_I5$AB=K*biiZ7~yS5 zkKqGpj2e-TzPile_$!0KM-9O=7Y$XQSQ)pI%)FKI^S#YHjt!7)NGNQ#km9focZM*o z%OUykPGF3XGkTCh37=&y5;sZ$@rBbV9q@od!H~Os6HtM}!U}Q2%06j4A#fUkF{ao& zQa1f=8a%kvoLLM~EZ8*7<X%!X6Q{5q6oJsM(va;<cw$;ma!(hN;u_+@ADRK!ZR5ON zyAb*?+LakZ0*u*Ub|iG!u0QUQg_db(wuEc|;sNysRfmHX+l7*)E0`<=1QhUMO-49~ zlab3S;5H(;iBEw1l6}DK<lu5~$|j-8f-%GNpeNZhHsO%ZkW9A{+3@EhofrobauFQ9 z(a(3%Rskip9oWIIpoRV&5Qboka%vq$96uKw$?Xg~S_`spJIu`J$2RqcX2A|pfDy)U z$g*LqSH5$Qk-1&q4Nf)-XFy0Ppu${2PtAk|C?Cwv4z!WuX#hLa(U7*rcz4fYMZUA! zBsIxf(aykaldIUOvt5|NSIWUt+ARQaK#srG6+Tc@lAX2y2`mX!G)^tL1d1DqNg@qX zD%di?p40eR>CRidj1#N)sM^Je`wlMgwnQR41^6G*9oV5f#d^DkgVCuxlWMn+X&nf} z_@KrRYPXL$76p$ppEP!vP+}FKvJVMah{%I!Yc04*1xv+&dyJREpkdiUF^QXLV=}wk zxV<cF;TtFmQ9cecC$duDXzvvGi@yIO_y&K*yH`f%<ZGrgE&7NJlMf8-CbDDC@x<1# ziI741dsjJU@$O#I>(`(_zZl9vx+-A75e$ZKPG~|sst!tsZL;_$%`G|bJB9=gQ~;wW zfwFj|D<sA^wA++vqa|f}d#7KS1*RGFam)?|<cnA$9+mTg1=0q%uw)b2)HXSZKk9J8 zL9m#IIIMS+ihaR}MuA=%XC}!CqKjCn2_a#jBosGFIJ^Q=>XzHfpV+mEa<5@wD5IMw z6>be}ehfs0u!FG1t413*&pMy308DL=xtwiz=o8)1&@<;Wp+LLdxKM)tWWs5PbTF#U zP>y{eF@xkAhsSsmLZ!-36YU<{lcHgAUBrbQVs@U&U)_N{j2O7&F+{;QsdQ!^kKTP& zU!b`i1<QfQF0J^F4tu#au~tt(OMKs@x`Kn!lczPth9S`)u}P+L5l4jSM&6OkAn6R= z6;PQt3}A3O@zNrF&UYADT8$Yc!ShC)me|EBb%z2kK`aNl5$&hxt@MQ7Neb^Zw^uOZ zrBf<A)CP`5bV)v?fF)p1w-om37RCrj(fv?nbPg~|d_02fT=ooL(O9+>;KLe`zv5lN zppYSp+F{6=K{-lEHjzE@j4htbXb(nL1gbT+gs+Ss`#ABIOU5bkMo%6G;jN{xCd|qZ z!q<?8ZJrMk3z2EwvG#QLPq;l9+)>K%H1@OmQQyN;3i6gxAUcC%jhsJj>HUKt{AqeH z1L9cVp17T~@T<$XA`h@kOrIO}5_E5d7nvWvER%YMzL@KW26XR=#+i}|w1u1;CE*FT z#D-?LN2Gg$BD2yZZHdx3RPkYYaETYXxM+0g?~`m7B91APiNg$b&P{9B)3zaVO0tWF z5k}(U<D>)a3aAa;)592TH#nSRds^3NOM*vPhLHu|#FZ61*rP+>1_6*ELKPnSV!>Zl zs}pVD9|1o&9YGh6VM`!m)vkd3#)=K(f<@9C0yxZ%=;3IZ=*b@ZqzGx*E{zVNLK9_l z0cEkUO|UzgNpZ6Hr|u6KnVW^Jnx-zM-8-B$T9eM8$!5WpMDJ@yG{d-C-Rfm@0|V;J z9oEsOH0<+ue}L`@pI8{Gt?B%Tle0(~s|cAJ3=*r#8_8I<W;h)3DBYT|dGYDp<5RPY z^ILF5iVTNf2K(0|4xlQX2R3iC`UHpE@d9;LH!CFakm$HVr5RZc3FTmm@j(**lc@v3 z5cp8(iZi@a>C=7rp=@dprZX4bXXWJ}GqHXP!3h%vIS5WMy2Mdkh6Ew7q!yy7y&Z!* zUa1<#X6py49FilTrc#6~bRceZFix|h@oV57%^etlfAaz_X@;{posrMSw|iV1JF;+* z80tF1b&(Nm-W71WFu0LS5zNB4hMg|n5;vDIL?kCGJa$tqIAB&g&g;>z?%hJ!tmWgK zx|n8CW7C*(NE!#o$hpaY&}=xs?$j6&XXy+*=q9<k;!(spIt7dB?G&n6NLdSV%!*g| zn)om<r6e@jF0|Y62o|YNtQnTr9^u}+mpH|ZuxRig&&BxEEZGO?Gs4dc>2s-)6O<=K zg|rq(LOejHFei3DBXpBzkC8=Z>i}jBy}kEvK+2EQ6>cpJ+>@3=S84r8lM>KBW(WOK z4s^@Vd{-z=`@?I-x!m6uwHv|3FoP`iYe^=L0p|q&0X@)ABr~d)fh`=lZ2)p=+$~xl zb)PD3auYIHPqfmA6Wy1yMIN`LJWgm5f^{|u9n(aMAF%SrJ~?wEN%!<3OlG(!&MY_r z!xi#{9Xzg&^m@^x48$se!$K?^n{$<(m>#C^ldSQvd(f2oPnWzE^4m73@^mOIvYMH= zyvqoUaW=5(90w4jtg_K*a>FGqcOg@X94pc5VOh6O4Qv1knm+04IYh|U_8`S6KYdGg z_2Lr~MGEu710PNd38eZee7FNTd`Y%};I%;Wyd;P(0q)tpOdPT4e`4LA1=NSF=|H!Q zX4rZY@*anlg8m^Pq)$1h5%g4<3fnGb;&W#<x1XDGG(Nf}<sKC63dWt~3=2NH)R!H! zoV*ta8@A=XN{>?z`7G89?~~0Tq2ePflV=3K!fdC7U}KL+t90UIgkl>qxKRp<zmDc6 z(XlW*ak~TE0BXiSoclm1Bk_VYov8fS6p;WVR2vG8m0A%G)hye{7VzP+%xC){mk=rm zAwlp*lmHtdNKzQwNX`%KnV7vDzVjN5?!#2jkfsMMz?fHuXlxo6y2Z}uLktJQNE-w) z%~if3;&N|MV4_GRU5ezNc-9*o!ZZ~0Tob;t(2^iG`kFq<#C$Cbp71;oe37`~0G?DN z`Bay#fC8R~I01;@sNRtHlX)#0g9=>Li>!ZI@gmZJD(<$FG$!c|-2X&v*3;$T9xP?j zkP143u2zj|uU@1J7~;=e#X0Q@AG&qPlt?yIx@{HzT;k2>oaZ0)?|Nd0Msk!`XKH+# z+pbr!R3q2E6Dz2~OG3iUrt-nA+$h>GHtXVjXdq09*!H7nMt3k6#FzmzY+wZ|G7~^V zemt#la?0q2L3Sw(p69%!E1;kuL&)Gj!wwe(ezU`hLuyJFM}<WvmLb4`m^GgRi4dqn zFS{jTq0RweOD59@qlYBuaO>`Qa<1CfDmEiL`Bb)Uz>2LD5+;Y~LT!UWrl>h#21g%< z_nEvi&bbM3eMwU@y~!&vAx{xAZvqbKvyDtKbu=Tjn16XBHw1Jzv=s2p-=z%bpBa(T zWU~<YXV2Y!ug^NK#5Yev(Os8crsD=rBsKOZ1r5Qj1xxpo#Y#D2SfFNsrUzaM#MHns z1C8_>f*Vwa5ue~2%^(#|`xW0F<-E<y^3ct`5%pzerz1UH8=ShTYUUl@s2c;pDnd}U zckl7MCHLf}c{P5G4ND51_BMppNj&y#5&8<QC)W(;G`G01pHrQp9MD<)3<v`>#etK2 z&<Lz92ieQ@W;L^m!XWWDrP&|kE%lH`a|JPG{Qc@*z+ocJA$^=#x5%PVODc)V5!?LS zh6hv98V5!;^t9F9`uD}<lE7qL@2@jTgi0K$(Ue^%^%+$xUZ~N)LgeKY7y^nO)h$af zBviPscr;3t{RRY8X1_q7p@y>}Nt4z3Ro4ubDe;fl3CNF&2rq0vF@h~+03NDvC1EH2 z1ffSuL6vnLgbV|WU1xJoUo<}Ip?pWQVRRwnk!tv|xjUL=(>x)btTHIfxHo%eumt^+ zP*>{;#7miE;t5b;nEu)odIe$Yv&Lp6yxh?xc^2ykuT+|-P@y@q<gOT1%hT4ZGl1{f z20YeHb?1eg2I;-Wm3`)7njcn>ygIfC9l438D;N==_Wt0G8~?OH;&FWOh@6lE--tJI zlaOT_NjxE|TzblilH>yfLvvfema74XfV(26k61%Etyw3G_H(;1PPbn>*H>bMfisn> zP!lmkC*)%QQa)c_Fm>LDq#DOcIth+A#pEpu`q{nVu~GmmA(Nco{te*nxgMeYL~_y! zbt0Dp64(4(c%!!x|Gw!5^`Zl#rY~k`ffp3V3YIW_hR6hTXecr~wCf?Zgk8Y8apfU8 zcHsWzCG0FAKQ~^%ui(}cokCsV#F7Z=F%h;P0@AmP<-;pb(ZUcr<@~`F7QIrdY#4he z57A!ORta!o7Sl7RIG>yA$ZexUfiPfxS0^{wNiXhm@c7Z+427eOwCy+i?X5u|bxuAq zuOBBJaMdtCEjl;#5?JHRG4`MD8Nd?W+bFTc0R}TiIVLhV&;V^DQyj^AJY50HfDQ=k z5H@7dZ&^5~C&m}zlRML@vEhDO<mk6RDyXFLlF(cgeZKE36S>dghRxI2w;MzlZ`N$2 zSyRmK!FU*F!D>f)iE~r=*t?W4fsp=%mJN@!2gC5h)I*Pun_vTKdkRlOzxg}ySUi#_ zVLV~$=nNLbQs*?MSS*4P{P?8lGJ!7k0xc{E{Bv{%$J@}t8K2u|Xni(qN&{}eXm-OY zrc^lbKsSxJCNASdX2$m*i*@%uA&%I@M771H;G6R41j3;<>|FqaDs)7Y2-FQz=RpL- z1X-XNfKoLdzQ+GFdvr)1*fPXa#a^4osfoUAlqqAvE0rf)-<>?%4#~O#q+}UZP;krv zfDfGHLq#g+3b>ifr&JhLFhDvOGQ#x{{G_peGn^+k4rdLasVxvi`8mnqu6W+=DN^d} zAjA`RW0Cf|%Aw;fqs#ZTYb*Jj-z(n~L<bb|jCi3*@)L=L=vB^u;0(hlu1#5rla_)e zJJRebtVbQ+&Te!yaXlXQLco0r#*cE1AD2P<>Y+z+;}7u04oa(UQd*d;m=_tf#8VcE zT^=K{6KP~z0YKCOfq%_$<lR(gN*U;vtw2~3Qp3L(b-_DaaXitxMY3Yr6sH<QoDbS- zHwqV)WOv;t=kJWxDxY^kAM=ANR-q))@@4=-!los0*D)BTP~>;5GNypheQ^$2Xq%AD zYQCTgOa@O}0M^J995=Maj@XSi6W*PI)P<5nAjYnMSOH5wi5L0(yIuer&KhY8w81*{ z0;Hx+V4?n8!$e;w6_rvx;sGfp2=AaFp`iQ9EF!P=jb!=&4lVT5bYSF$a&7TtTaZ&H zy0(gabm^Z&b}WiO88~f-TfHP0LF_K#$|}sLE=4NKpwLkYiu0<U5G)Kl)_K(<1|@&^ z1^L51Oe_VULtNijV)f8$TOA$-*rOtH=W$k{cLiU?(2&mHTOKOwNj2+AjW`@?G^7}Q zim%fEU?UdO!zF5$VS!gBtr-{agKz+&(Cjhr03J9Epq<kplylIeLuj&Cbm*f;MZ3aR zI7H%C@kMj8@|YV=@!B65WBXka=L#wo+l^JcJ9u2yIIEbbR`*i!RCaevA0I~9Mh0u5 zo5YN8tFlDSh-E<Q@q+sdaMIQWn*I?EG`)tj@stUi;>n=_8_a`AHUJJ25e;LA@F_*c z^8lJbMFjnp=EMPHTd|U;Ax0Q7E$%I*0GB!Vkv^*MMqRT++XGK)HYmA5V&#BCh7l~E zIMUh4)d6kjV4z+PO*|bM<c(Bgo>E|2#_SB`E<=I(A2#<p1W&0-^1=w*(ecLtH{>lT z_Qiko$L{X)4?b{{!C-%|f+D4$@rT$?j}871_^^Gth8hC{7H#H#l9mLo>~Lo7aP@6V zKF1hS3Wf!q;*Jt{1PfU-OB5??d1mXwH)ynB?-3C0W{Hu!?k20<j73j5n7D>aa}Bd0 zMI}6TX-Ed#l<A{Ikju`bEHbC6NpP#t7_Ahn(0zIKH_E}`6~M+PoFtmm+7&nW4m7Y+ z&@epF-9X)e$)RB(-*l;nZ|=YhI^)}>pKM!0bo?L^72aQ?ub@KE1c0fqV|x-TN<kI> zwG@K@6%w<>$@Oq~v>&K1k2m_<O$-_l!D(aC%vmgA=>j#4Q~gB7?2==y13~+25a!1! zwv#c1{A$Myj5T%McJBy=2$Pra4hsF*iWsP-s267|3Dd~lAz#blVZi(yDur5>E?^0M zpfF;W?`hf<6qy+E$8W5Ca?YA`4$UchPY@3P+Opvl(<NioW%-D;CvsaKJ0n;G(QUsA zV0oD)uFsOO3syk9(F0o4<1hu@2&LwG6#VvaJYfp`3`@X-e)OfqU?SaU7m3jPPu2*} zWkQC#dW0Jh@t}*)sU{$zhfb2QNM5lFYB;GCUBW336D0uyLr>@mItN2s0|VlH&u6N# znM670!D@yY0$NB$yi*?C5f(j@s3HJ1kO*ZrbR{RO515dnCvhnS%LAOC&!_?wk$uyM zwPmvg#)QbY$&-xD6npr%J?<bc45%mrGV@3$5Z_nHwWy-V<d$(l#9$%E1_O-ybi2pF zCa)mNCXyXO4Hk1n@Wf(9LJPdWz&g(VOIU_On1(tSROt+OSPKV)Cf<hSBLou9^6}e} z5LQSfQW}#4f{{Ys;N91Gp~(#l-NCU4w(~=${TACsk9?qC+l<Y^O!7>evRGmhIkdlL zmyp8KVI<^;kg40zZYGTQ+6izi!47K5u;i@gO2gF^AuJmlC3w4=ZAX!}R+HB%Q-fi> zp6_^aMlHKNeXAlTPdL>Ri!+X84eO_lZ=me4Beb;>CrBcjrVN&)pkvygh+$y5AO~^~ zD@eY6U|D$l<gO}>f5mjc{pZFB#@ET1nuDbv2eXp$i8+EK$$sAJ45yOmM5rg6n#PqX zcgre@*u6~gc%Tu)BH=*jiBvcj0~$hqyv0!gC;bf~u~wl91yM9h74GsLy3WO75r|}A z!CF5y0<HuQ9kDMEhfs|gtOooq>$P&Qv(oMbW1pXM)i|N|Pf`}B5S4P9A>{y+dOq3! zIe|9x3nRUE2gP|vZijR^tfT)A|C1u^!Z)>X-M$5*x%D2U_mA6zVNK=MCEyENDUjSZ z*GD$@W(IqiTZAFO&k4&!268lUQCBOe#x;b*j*<FJeNKx+!g#TJU<dv13YvYv+Xqq@ zw;lU4O$-ng@UDy+G{%<;YC`}3%Tq7Lee}=Qc#0E%2$qGx6&|$IBs1~O&wn&lj46J~ z!KT8V!={XZK7_x<DLgs$8I#_RJB;E{2F{r8E>6C_)Lll7U52kbwRD)*!{Y_W{T`7k z<HLfrW0ze!$%SAZK4G;$-cgb86%vv&auT2s%s0F0xgxuN`Ib*vFlrglQZR@;>b9|Z z8xb9Lkq+VOD&R^Y0CHzoC}M|1cUEEr=;MeR^aANk9dR!{!$OTDdN(|%lDneMB<lvD zTvJ1=AS8)_<_Bymdc#&>;+Da?9pma!cU59A57j{-i3J<`6E{5|=|H(kBf@D20VUWY z<pnSI&16RI(R17qPNWjdRvBDVcW?@NMJZ@mhQ#6^f_2oz@BHDBf`Otn-y}R<7pR_U z02D}S;=Ort+CZrw%ox2rU_)9p&KWg?J&N}wE7((m(8pj?!=V7}1NVSa$k~ofE?EGy zpg>2vP+}+Q&PM4|CZ<4$=b6BncEb4K3YOSzhjgH#!!pcbhqUNdkOf3YLOd4J1D1-G zA#Ts&7JV?K8NR4)XqGQGquY3>eF(~vr9fHARi+G$G5F#J^*f;#@fe=r7Va?)aA-Y8 zpS!Q+!-A>sDr3t>u*QyPp$UGFKaOZou%0_%p$o=1Wv1EjYs?b3W4FovdEY{#sDi|k zyE_}RZ?TUMJh%bE@SFbQs|Ss6cVz?;Ks{7pM=5YO;Ken>j_gcLXs`G@)$qVnuGirk z6gXp7_^s6?J}3!C9=DIK8c9qTy<tG$s-bthb=A=bJGJwpvljaEuySE%htMDgglodU z(H~?_VVIo6AQ$8Vos=5<Z#$t3Y^PS)U_gR`8a^+xYBav|kj_Bb3YBO1GI5Ik<HA;j z&F;XjB_@$>5}&Xeb5oVbSbRKFg-Po!W<czcmjgN+S*S$l5{i_93on?T&Op_QTPR3g z3ifCFC{sjBpd}`O<m)H#q_Hblw_1oP+gVdxFc3I=NWtA^3rg?+0i|>faI?e_^dlEp zhR^wPZyd9%7_wjo_3~Pda$pWm&-=8GGG@bAv_afnhL7^0PE3~|isdr!lc^I%_V`#k z_B;GiGhO+aEVclpvZj_K+ME@B9&k(xkI*EQzrhNLX}A@Df3JcCP!<hxm_vrcxl50| zWnhUl95x0(w;ij9!HVC5=ma<s42Om0e~Qi;A@f7Ik2Q7}BfApI7!K~1u|qYqdgw4# zi4CO0!w?pDB4us`hx$u&y2^u=0%pz3;mR?qrCnf<0jIPA?(~ilP@+3{oO_gaq_bz~ z5Zpoyisx$9d)9SNO0?4LDk2`JcQEqTyf_AiC-6nV7(F0u2urzrq{A^JJfU)yc6^GX zZ%$#iz{pTShv3jYK|sLBQYto$II+HHq)Y4gxlzK3XW|sZ@d<QQ;iW2`?}P?ZL#zS1 zYL0z5yCozbc|^+Ek<)~+2KWj7hXtkc5;?`*k*Pv+bcX*HD+vxbL}EvcX`-gc)Fk#; zVRy+4jgz8PbL8+g9J)S*3yQ&831OAv+(ysl&Utj#%pd+^7IxW;^+d<Iq7#IfST%56 z$u4_<w9r$yKeN@~U}+Mq795HpZV4n{J8_{4HK4`!nWRG61{Y(`4_2^*eONwulNH%j zT{rgV4hB~L9jNFw4YsKmVRr()!Q&(YcQXQIF}IJRxxxtyMns#Gqe*koC21yIAjAEK z9bpf4qHCJ`=*#5L?)wV=K@8wj?rj|qRL4{TZ`71V+9>;0Vfs$&u`l$v*HjqW(IfO| z(ZNQ9hRKs*@sKcu_D2cON4;qjGkcYL+|}uQ3p3Z+h^nhIk}7g>cULnj3NZuv6Z-cp ziFc<4K_gQl7%Y~I85!|wM4X4)0!v~egTS~L$}J|2!(L|oQ0Ch^L%`7X?~~nDl!nUC zjow3D=0Uz_j$6jxK{(Y21x;x$c#7SJO}YUR68xK`^_V&M)CP2;QjHQKN`jUyo9I5h zUsh~5@sE!vD}~Raw-DMPq(7L4TQt{$bY3*PECmTyZ^z<w+W_GvXSo0?s1PW4mdgI* zFo^=XLfqetnrD+M)tY&I%!${>#$?8=;@T_>YMRDpYPc;-zxpOWOCGS0VKvbAA08-Z zAy$s)Q2Mg9d=Zkd^P+HJVL{fS9MB)MGrM<U(O5hi?)l9>(vnaFKlm;g`<K0Jy+=-_ zit&9{r<o)wT27Ialwg=12`@l7-Cd>lW;QYvnWG3%GYQFu>~t_#{10?er!_t8Z_^CR zA8r`(RzK<r#REoLx0?`Hh-3e`Zn*DZ|GT+#Mw`%<KtFJ+DWtxxs5Km{`UdvsoD$#z z+zZmV2}q~?xka%ecF@SIqsn3DP*gc7VM1b(K9m707SE{ST|pKFCQaXUv8btY#pTqt zX`&0X;`Rq*VG0`%jbTgG<F@We4fe1!*n&{?%HWkQ$qQXp4iyP>Te1=g?OaOOMsu;~ zenKfKKBJYc6MtDD)sMNs_*fzZF~K<aO;x>F4qpD017ZhNh6R>`xP?5iZA=az*s#Ej zTm5lLa3|-W2}wDt6HEU<heN7Y|7;k3krP_@JvL)Ooq2Dzz}hM-Trx))_V{EQlH=&S zq$Y%f1up}xCnjd0?b)nvC-u1Wd5>V!dBoocGlZ<!D<v?cw*|EpBE@t;yp5T6MVzok zjR~!W$V@CL08VngbE7*@jof9A<zl-k`Uzx$faQuEMhDo?Mob+Z9)5*_F&YLoEuAt7 zY#wo%gF!^0Lol45aB6~eiw+8xSW2;iE;c5K>}AZj6=TbQew1lrwS`I5%E*Kmp5OEs zj<_U}xr;T@{_SOO%;N!a?}o0RmXKq)dU-}?;KP8Xnso6saI@lXyPyJw`RNQ6{7;G| zpQeuiQHrz$B?vAF$tum;Ph2NSriLYG*7EQbw-=xAWOFrR0^;Ww;`i>~KI?{$x=K7% zpJ)@3J3*hfd5-|IIR|IFRyh(;Pb#0;95KL&YX)kZio`JkT20Nv)U2))U<FKG$Rmz> z8D2kpa9~th)*O+}z?v2t$OgrO%hTWmLdv+~O3CjIk2BlpwAjFTq>O@4p%YA3Y#OTl z8*BV{-2I4)%)a#~3w1|tLZ6(Y%V-<|a*!s6xIFt<UvF=BPodt_T(ca^&nDqZK32eC zcq;YdY4oeat+Zo-$^%JDx@w-)$Lwu2Pi}Z<XB6^94g*V~+&@yKiuAF`Jan2L<}Gnk zqB-z)FMZgDO*IJP6P~UR;+B8-B`1WF0K7iZ&uv5`MhC8LMzFx1A+5l$@Z8!Zy_kmc zkgN$JZVy9NE*aa=G)+lZP_qMdRhN(6DFmMI1gmm{3v@%@x`9uV^+Jah4&pf7$UzXh zBZfL*wdp|$X-KY0b&Jn;7JNL3Vb?Ih=ei+p&t|pc#_kc#;Q+Q+3b=_};$tx)wwtHi zLr50>lTL#AkYkQ(ftF(4dd^Tl5QA!gPd*+7UZ?CVmJE#f=fWqIKv}5En4l}pk6eOb z@{17|5<t&@38)h-JkE!L75~VOe*c)N)Q^2*VJU#3xq-aTJ$as90`0m0Gh~{x`f2=3 znb@NF7jc4N1dLf462_R2OKWYb*=xJA6YnomI)m|9B=!&SxRYY^Whyrz9yIwo6*5H0 z5U-Ce58XD_8@QWH2@<2K>S?e;h6F@^I4!Zs)eC*k#K{K*$xIIz`Qk;1CtDQHT^We| z;vhUhR}^mJ*<V1jzlG-7hX*a#5wQCFIKq2QYmVqgD68z)GaCUn_>_HcIFe5r4pNi? zPhP`pNXlaBp1EQTK*4E#Z|J3nypa<Z^-V=+&-=qgsh-&P7}JC14(8w*kn+0MR_p{g z-Z}P2jImZJ30)koO4O1<Jg$V0+rs0ZGNjgmM}3JG3hmXVI6RThbnF+rBk1DtoUSzb z`(qOygP~o4$&pUCSY@m&IW}={xIOP&hmYzzC>+P0%Lz@x7O`fays>HS3KY;4p<vbt z&f6BbRB<j^ACogZ#m+XYe{he!R!HB@LCZ$@rkM;mT<Tb=nZ^+8ilR|8*GoLN(jN+E z!45V<pp2kwu~SMu@FJDunH3pb)Saa$ds(N8go0xRn&-mXz?kS9&SaD7qC)iPbka!r zbZLmV&=oL@F*^on{wGKwKhR-@0xzK?_~+Z>m#m&3OaOb1$6hG(AM706h&Cfzq+)bq zFjfmWWI;l%?=LcZ8aQb*z2LUAoq|_4g8#m5$iipKvhyMwPi=k*6wIs!xZ^~hHmeK( zBj^IIXWAlo$c%~k4hHEDQxz=%S{4>HO`$zF*F)`&v+{)t8k#Z9PF+5jf!S$2?p2U! zd9*~*Sj4{ocRcS9HZ}?y?s=uh3+CaIvgW+95ymfd;&W>cx)6`s$Kc>wORA7qY83*{ z5baX?#2Pfw^{^ZmsP%+zWBI6>vtZF&J0ywMO<oS32whnrTIlM=lEvSBUsOeb4U=$^ zs^A9Q-C4~ehXR-qRGP}&D8+&`2}!Wv=|TzNre};<T8<D}YVf4La>iN#90Sr46E;c- zd8~V58;Kf3V;S?!E%z-W+xpU;4M3PR#nYj~Z>g9ig{8POcUH-nSsDEGR2mBwytk`` zwuC*9tA<`B!T7CrK`7cS;O?|mo77Ziu}$l7V|TJ~2}Po#gO#vhm!P;E@StxHnuNuP z_P_@Rmp4kfH0f2<x<mVw7E;IT(m$`oU=#$n<;S?}gg&RAzz%BAjr?mgNUvd$bO`FR z2O*_QSuBo^Olf>NgiDU3CEzz6a2q7nk2eZ?d$=Invc>uHdvNG?l)*HCzs)-&*Vf`# zVywVLu)tw~$W}%e;R#jeYN3*gGzG!vA8&USsuENQ=P7ySdy_19OdBi*V~0^bd^0J$ zeB4%`;4IYa`G00^hHGU-Z%<zX6C@Utg2kK=G7Et<5@*aEB&X%Ahm{IzODuYL_ux-^ z)=rId%Z)>tQ}DVrJRy&yf((v{ZKQkMr$8!J3meA;A(eEQhCLQ^%bCp!LT<P5r_HPw z&}kJ*&q2g*i~!>XMaoMy1vEJrG6T>X#Wx6327`L*4=%i5d?3SLeg%*qCG0Rr#z@$- zF<z)6{$gY2u}#!`<c)?4lt^;%43#28g!NH6L-u&u@{VXj$B0^3xl|2I8=uZ#JF)P& z?Tx<Lr7pli`}tiq8hy8RltRD%77;D9k03~f2Dpn`3VF_gCDwbn*u@9MC5^mo6rLSW z5f3k`f{%pENOx;hnNpLkz$r%gBW)DAL}!!(8su(9cXmO-B=Ah5RQp%l%n=}GEFh+= z)reGD%|EwcVIXv4R~+*tPU;<?0)Pl#I!^TY&G}1@tz9rlkgQJcB&0anMlyR5veQh# zW#U!^p*>TPKTuqq9)YDQo}q!eOsn4v|3VWpaXnNdY}XO464~S@-ja?1N`QO#(PkYi zW+x3UQ9@4%eHt1m+XFImg7LQ$RPihLZHE4Y_bH|ZVnzw<)1E?B&*^ky8T$?i7Vi!m zDI?U0zJ?x^!YD{mjix-TR6O>s4AChuRSje~`WiY3d4EEYuUwV-#{i(60*MP*=D?y| zLH68(xZe(OK-1{MZwl%*>5bjxs)%lDh!u#<y27~x1H53XVw=ViJfw<H?9RZabo;uG zB9@LBUBOHr4pRA`pCAz;_w78pZEz4b+m9!jb_~$DNA^LohUZ`hZq;5TO$knM=b1py zWFx%D7l|48aFApz!SKOs@$O+PQzWyvcyNFZmkASzM$4W+JqBA<pjlH~H!F-)G7(RT zGKbL7GVEN=3kUtZr)7P8KtZi}Uo+m5Rs&^`Mi?C`XXgobSq2pGD)^R%suUv?DwK~T zsR+w(eS8ZRV;$(82=p_<9KX|{vxn)<gn4nn>T{zM<i@nuB6i7LvQ}sC4BQK4JJ-O3 z41G&yu*BwQMXFlUeQ}m@;1r|wx!d>u!Ya9bwvmOcMU@{=(H)4(;I+A5cLv&YDW9DK zN39A9QX3z^LcPQbp3-)785tA!goN?EdDkg9OnQ@Ve~d7doZ&5OsVXo?iUmnu9_NtZ zhfn9uhk6tf%i%>$)1zU|IePEOG)J5rtjz52z~jvAwUJE)o1=uRs`CQ@E57$&gfBR4 zF-_ZLMD!&+l@n+X6+%n$A7Q&jV{7DL<C#EM+3{9-@DqC$MqL1`;iOhA4b+~=TU=^( zq&KKFO&E4L0tPE6os(3w0y59+VuHA3fvCChP09m@1z#r4SSVgf#2k^3jwNg<2TQ}A zz;Vpr!>Z=3a~VAXb6Pl<@P~X1a|DmRjvyWTfY7B(CV9Q0bFei;K@exokw%}%PHq7o zwn*X&S<<gCh4t||VeAP*ITSzWG9Y-1pddpQ3a~l^w2ug=Va%ttZ}m@iutcm-#REm@ z@?=bvEt^JA5l<-~l$ZrOsDci5-zq#Y9DG5BD&IU~4M7?8megQVtl~nNSbtYr7TO>{ zY2%KlX6h7{k&unOyy&IeWZh7Oh)~>J-AVxPgp?TM+EF7w?#k@r0UOSO6$J+bP{miA z?8l6kAkN;@imJOLgXOWQ?U0vOi0M3{+RdqIWM;aH&oJoB(Q}M86%wX%#TKk9m%524 zO@Pe8>>Gn?!*DVa9XycEz-!7Dbt16Fi^;rUg|zHSv^HwpG36J%6?5~dk%Z%eBo-d0 z&Sqg6dS}2zBZq@3K8Ta@M*O3<5nfBYbCXzT=diYM^U?h23NBUi_+xokKacz^1==bk zsU7)tMe~rOAuGPlV51UdqC~?pm@%ih*|Jy^!yVZ&JOLhL1dycY4o*4PFihkOph1mG z?tuP<{!wJs-*2;b2X+h&|0JxhcA!|pmhH|&oE10vi4sx5Bz-4f1#`N2s-55|ok7Eq zPj0ImJHde<*OWc**>2t?mWByp${oxG(|$1xcES*Tu`56c>>YIYz)h@h%}9?L(32h+ z%76)N!)}6i!C?zA+o6|OtYuhuj1{E9H$&vW9W-pdx`O_>`&apRQ;(kCq+_uwxjF@k zk~srr&r3LG#}<Wf#v?}Eo2;giKw|KmYSoQZOx~Drb>``e<mir?3<@$Z_%>m&6o8uO zVkou9T48JAK_x{7hD{J={b6db{s>hVpV+0$WumSc>IYcJcisx^etgqFA;Z)ll8N~^ zhp%6ylW&Txg5n6L6!>a^cep0yActt74Y5<sBBr7;(K~|qkyosbcaoNa<y7d$a3S&0 zr854#tPA7a8)8(adqUa)Y+xJCB`RkO38@HlNYW4PO}hnA85$=H2w(*zZW;US53{q> z7I-hs<>15P7$PSwfY&y%qUUeh!z$0DK^s``tgRHBs<p5e2ZN5-(z%*(xo6-9n=Xsk zs+_0pY0)%4>>@H8;%it1R?MCf`$T<+4P(@hDWOlQOw0ixo+%>@xgJIglq?GadRpg; zrXVNJ!I&6BZd|?WWMhq0W#Hlr424AGmka8dOP<DF!!55giQOuaOi-_3;SN*a<lqy; z{zNW~j}KxAUP0I<x9cx`(fG9rp5?MgdVJ;J7GX`ggD!cIHFyT@uHID0AS{oA9bokD z&~!DJCiWcfnB=cKx3R&v^CUg9sRuDx@q>|}h5$p?Jh+Fnn$YOi>U;tlvQ3x4j4w-M z@Yf>ROFQN&XVfs34p^X~ov+Mv#$RND2m@FFUhq>#w&RWV_3ei7Pt?y8wbYp|Ai}lk zR&=K=<MF7Cx={vDVy{C2M;06HlNSiuO{;+uRGYv~lXGeVPcPlhJj_W#6+FO&W{HEa zVG-`pmijSnBC>N1#QJomV0524>sV6%*!7c`(SnFv(bEjplWl5Dg9ewRtEG<|34hNA z?M%)xu>yZR6UB;9&LZwt86;D8ofwjLTqT25T7}ycZT*qh-Y$N@^E;8TUl!{WyhMCi z4-X-9P|qzU?7~CZfZi#rYhspIuAqe{_((&_L384PjkpE4wJ4l@V94sv)<4m*U%BHH zsfGGEyuE#gq-0x`&S^DyvCZk1_uKGzU+=w~!HbXckrsVmh!?;VJuUR`J2IY~pkQ<( z>k8J!HXpRb)KCg+a5$L|K2_jE4=iC{UyJjrbS!;395iZ)d(lDouov6#pW;I8T+s~# zumuO(PLmnbiVPs;?cF#eAUpC52gSN2v-BJ*jL+Rr6@Dikh_<B^r0`cg<OI?XSq^@6 z2U-$VEElJ#IW7KU<&dGRe(%a>3x-D#Hn)5D(FJ<jBJ3<wGu4MBN`m_y(z{?v2VwCk zW_oa1gdNb>*%5c)8D#MuZo&+q`rEO@d}pgDsNfxg4iN3jUao&o6;Te#_E2c!;wK3r z+(<{F+t-!Wh{(e=Ff0tAA!Si8E;#o1AE<DqfFGUnguIiTK?51A6YbjM73ALw#9w|W znhjB|LiTv4cnVGrqs#*%0<7XyT)MCEtPE7xSEeS33xEbQp}`j8z1u5eKj;}O9azEc zp2omh65Rn}A4c$&oGaTZ9X8n1VIy!@F3`-rS7ybi+c(IAkd$}c;3^z@Hh!Jf;s+KU z{}vm_gM#7R>*QH8&&wHvSsc?xln5Oh=8}x2Mr*b9X&J~$E_Yxw$;bpF>T-KrQ-~~X zW)t4R5$hNVDw!gz4p4}(Hmsm^Sr{(q4vM_cRX%ok%}yLMz?NFW1Es@M#{=acZl^k; zsS3P`9?(@GMJ9=S4-UV0=1t{|?NbKkXVs7$Qgmq!F@x#S;QdE>a*u2g?+)+-DPclq z@Z567^%)i#phU1<4&nt!u_xCpJom9rvN4dQkQyPl_%bWtFauvCJ+XnXPIT+g=OuZd za$nfsZMccz!9bPER+IsQ0l?LnUGRd1t{}rJ6+ET0n^2At`ABd;vteP_z5Cp&27oEz z0KJKu#>P#nNzcl#I7}El`A*DA0gd2W354d<lN?0bogCb?bHj7i(SmxWN)d<j83_zD zO`J$_9NXjCEIyTaDsxCl$TD5DkLRH^&b~A{L(>3R^nfanEPhdYeIl=+$!_$L-Im_? zGAU1s#d7Eygn<a=a)&SGB?;|xQ1^`H9(7}Gp&cPWfw5doLbdHSIzLH!GBBtIiklwH zUzsb<ZET>O!TK<l$;k%`lUXCiD-grb%U_uxrY$r~E@_0mC2W&-bOmmJutMgVGtLf} zAEy+6OWeDf9;hKxmSf*P3&rvAK}ouX7*P(e$H>PK8yoD2b=Aa$*YP@aE8wSRv&c45 z5venna${$Um2l3o2pcjvei8U=hYIfiUtC(TI}(OL>MUaq_>b~;mV-U~w$7iMWPKHM z1~YVF2kAPkML~mC0ybXc*<)P$0i?K$e&w+`DFOOu%s4;~?`Da#6Wfx}lp{-CI)}%B z1y5yQ2}r_Cn(=Uw99DeCsxdL~288Q9ZX1qEvDu>?@31$S<SD&By(O_kE<ROx7f|Vd z43RP&)Qw`qZ6o_Lpg0ZeGEbb|!6vJU7q~l36QhtOm8^&s{cCZubR#+bFgBQfV2m|3 z3xzpF$}7-JGq+jaVw70I@E~V_5^0<KrxlX=pRhYvAcYN-0>&_T@M4lOkOecSm+|}k z_gxL)r=--Q*&#)7Y!SOK$`43+#YfAA*f_cPA9wPjPR5}@0koK=pn;o&#I9gDy*c>7 z15^Omw0Aty1(?+bPlEraxgl2od#;LvOZWK)&65c6xp{4R{-f|L>~s)2Z8jY(68e5D z5bK;P<jUKx=GG-mGF9Q7>ZM?Qf?1Z73MNLmH@gB0OY}>+iU4V@2pn>#XpNdK?0B|q zOw7)v#gXt9bw~#GK+g5Hro}K4@j+#Pnhyj!dVfz|V+Jd=OKx~Df|M{+l$zmp0k)7A zsll@|#b6X}SFKAapvSCol(siQPaOZhd{FwQ91JN7$#H`xEW|Z{rQgx+o`||*JyVJ= z8lT`Cjk^qZS;&%3h)C5VfI&0d4*0J-;vEd-UnOD+)u>$l%UFnr#k!Y<;vAg!9eI<r z%zq2}unu~-1smDhb<RO&D@tY|lj&fUG^R6_?Tk4kU`wp~h)n^djS1sglX#d`gDCe7 zlIP@}n-}gim7^@e;|kb88OPs!9<n20kNpGi7#kNxKx?wKVVP#%88$8lFQ~ptHIw{R zBXp7dJ(=F#0l<IQLlkH?R*)DqBzK9};?n44tRn_CwoQVNPanI`O3n`Smp)tA%oe)y zh;h>?1z?<6ViY#7m9FSn!V;F8>=ubhRo;LzATXJ|eCV46ce+Ie841Z*x@tEU-QiG6 z2MLEbhI9!numtZ2rm~mU$6xtV8ZSb^2;+>JfX9-IPd`u}7Cu`A;`vu*YoP^S6CTp| zm>=X%cTfaMEHIj!wg)uepot<;Mq3p!cp^i+TwB5*@uExANLM>&#gNkXf;(JvieJVA zcdM63DoI$+F)=A69DTYvf!O0R$3fGB?ea!zD@qIryZX3$o^f445adfxH!7QN4v^Hr zT(5=K!be>&W+u1>XFwPnX&3r1Ua43xqTxNz^82p9+vE7-?B>tfGMzn@eMsEW!Xrlz z%g{eF9d_V{c(xmf30cGo{DEjU8a(dXc1eL<CrrH=lz)=w)eXaE26|05vNaMYMu31^ zZj?o=d@UD|mUPNMbri?sNCXhFW;a8xbXK_-6ljW1LrwRfnmSyoo8Xo%poOu_)g{D+ z;kjKA#5D6N1BxXi85WEX6vX36V*ZGdK>OgKX6IAN!4|(qDx7)r>9pwqEYvV<ymKIf zfiU<4cu)gGxa=C+hlQAbSvx+zJe3s+(Z+`sNf5cbLUvO<H-;dWfW#7*%DamSk!(ZS zL(vl@(<@abi`C;w-7|9FSH*NrM+qpd&Z%cM#2~$B_*TG#FxhUK9bLg0bumm#yoOc^ zm;+ctPE$b0c>h;lAVR5lWcbi#0GoR(@}!FFhz4g}FoG0`j*Ll}#p9M9CPe!;dO+)v zk10Z3Pn=tU*sh~{?6?AHh=HnaZV_>xu|tOojt<a3J5EtL7uc1rI)iFe#2CHE+NKu& z+T-nmuO9%o6lSj`<nUCHv@v9l;tq8ZbP%Ea7ns5K>xs66RRUQ)6ND2K<{u7=410on zAc6t_^m`V17VKc$LZQj4d(i)c%a3x$xc4fC2Vh{r7{jmwlQ5waBuQJSK_%+_<D_0? zV7N3*SvHJI*i%i)LJD$1$9&wjsh$s7enwfwm76BRy67A?^gZ3_$dJM1dW6GMvO$0} zqaoO03tqu)vKr5##U8{8pqe7s?H=7A1$GD)D!T(*h+NS$b_IwK4ogo2icg%WOdD7j z9uxqb-QW*Hw>aYaO`M&9B(sn9iGfcZyzqE#q=&R-$!uyeX>^H$glSbH0c@cB^$acr zBX$997jQ!vZ$auWL)8{C&P<k-A_~Pt$xF)S5{c3hx1){8%M;nB{3GB66@BiS;;Rc* zSzbKF>w~ckIqF@y10^POVKQeGGGw{?f5J8{Cp7x&UM4CwJ$M=5g3Pnbwvc+(-LlV0 zR_Y8B;?_Tg2ed|N5)cMOXCCMd8vRc)cu4KGMDl31`RP&8#X)#}QzSH+jY;ey8ll67 z1d2Wvo%WNv^g5-6GNh~~2NLRfPIh-3;c?E<!qvJtUEuf=Z(Nu_8sNh7GLR^EerLGN zoZfzhQpR<0pxP7;4;bmN7ef*dR^aYFiAO~hqrCi5VJ)9|9*>80-arfD@%b8ugZasO zo^DP4*t8H#pLnGB0bETutGRoSg8h(8-r92A!Fj5)yMZQy^}?wIT-jj|{d2AbOi))q zTHewbta#nr_b|B*;fYYiCgH3-TCj>MdXURjd>L)eYDzqrn~6^074%Q1z&~f{7Mk*u z(r=!U0}JY<v_1~&9Z?s^{k9T-OPl3jjJTb;fT0BY|6q?E>0ZKgx2I27Yn5MjOF39L zJB}r;@R@ev!3go`Wqi;sAJymfO!@@3`YePw`N~M>vDSR=X2%6@S3$ODOEJY|gd;!i zOJSOBmRWN}TD|j-x=a(@(_8{VMWAO}0;LWp@6KOxn++0N#Zw<}=Bxl<dcrc%U3qE* zgH@~{xHo@av(u7^3GJl2LSs-GGFBZ9<X|wcKx~pIy(NZeF^-uLvG5^t7!m&VzD*d? zg0Kx)xS?a$X-Z92F{FF$DFWpl;d-v9SxmiwXCWPovgRgcVIH<%dUKZn5y%sLT9c`Q z3=qg2&$|KR8F$1gPKbi<7DYWv&c_rk61#wTv6e%~pi5Jq9E4lpu;TtBaADD28-fK` z@;mq5JA-rwAFD_R(oi!!V4P4XKx-tl)yqJNfq`LRSTsUb579!U%DIHBks@3l4C)6f zD4NL7{_s|wlEjW1`{bmaor`cL^!Mpn9`6=0E;vaEH+yd7CLz`0eLoTzChA3YO_MJe z@xs~=0R+sYTN%-Q<g(GH&l%jh#zKT$C8HaU+Az|Rp?m?}BQS$AakLOJb7*K}P*vOJ z`(a|<YM&Ct42{A$`aO)#82@e?gc|83gS7)0XAU7&!3{J?DbY3=tA=ij?hv|w2GG`p z$Jz^zaPiV{FFtP}*ezZTFes5^><h#xWgx3Eafcr&$q8<1AX7!%=v~+uEXSOl=%!bc z0PhZFgw?bv8-*bdy$c!Ir$)3}4=LNnThQ=}r<6j{=&?fFC}c4m(#?X&W@DsXEPYUC za^7)hWX6iuJx~flH!^&NC(Mb@f_=GbksSw7BYtC;jCedgWk8uzQ<9VBrxXl7(GM}j z10dw=ZU}Z`GRVhH`S#^N5MF5VcD^7im=q?6G)b`+812P?LV!280~@-37RA^E5-9sj zC<`qXh@HXn13V=M5r!!5uo$D2Clz=dM`VWohsA#sjZo^b8Q>ydGEjbq%?+Rdd>`=G z5yyWdVg8^5(D=X?i%BGpg%0Xy8#>Y`CL%%DHIX?r@wEZ4E@xb96|>${JhqG1;SOfE zAAemc9`4o%NAy!~MXcb<MU;ky2{ct16s8F+%`Zr|Z|Rd>xTPD=VZlB=0Wb)tpz-e1 zLV9LX|8xn9nx=kRsg*6u#d6<DXSs<#HqxR`pLF=5D2&f!MJI5slXeF#oF=C*&JgEA zHjOqs6E`T{0Nxa&$;`yB34FT4S{y!vbr|`@&}lqwr_c?pdc$1JV|Q6eU<WXh!-WPN z4lM;-Ws)Kvz9#k%Ae>H56daBY*wEcXcxtDJIc5w@HTzb<+R~0zq@uF9=YP6G!MtO@ z-H~Ph8#dEpka&!Sk6i%wg1!U*#b}5nu2=oeQh@ws-r+jZO9uE#e)k`dg7e`UJYkdq zZ{==Rut6TUR%(eVcW?!Jm~jN}#fZRkPS)jRnB_&!K|-$8$%T*Muv_eqdr&x?iCDu1 zX=Kp!nxhyGJ#&Mz*FNmS@qr8HNAcX~5+FfFzdD5a!4bqE8%pTsPGRV-UkdDiTRFmt zqIe$k-Qtb9WAfhlk8&_R-X~Q$1MWuo?rgweN1O}OgSukr?h0r1LG;8Z)i1RVO|i>@ zggWFL)~CU>XzrEtKC_p#Y^5NZn<BxvV-Nz{7x){1cwAybyGuaFDT5$~*5Vx`8t%&l zwpvhcK8^?YxQ}U+P-+F<I6#|!FTjqrG<QfoVUTiOK-zbDaQM6h%9RdJ+i^IP2E$L+ zv<Nw%!Gjg7=;sIf+aAJP@HCzP6ml?tyhGSRJIdmU)-EH2C;nmu!(rH^Z88efR=$`F zQ}3Wt9H6@-k?UoszpH2tQj~%$83_bxj!3xy8mwV$6t3w>CNRA4)z-Y;IOmE|kY;eF zc|UfXKuf~@NS7Qd$RFKBlNO@f(njYf%}dbmM0WEZu8M^m1IZyJU&l9c9`9a*`7>)^ z2aNdewBU6krml2uE0Q^~X#befcwq;hOgPK{vO&;wL;FY=_ova}fG}AKrmPqmy5x3j zv#lH$sBJg?74D>`rWkj{($*&0?aj#{g*D0Q*F<3}Jy3xQ9ZGAK8`KPnJhQyI;Eepd zV2C<*#)8&j3<M~Y*sPolBo}EUEHLw}J3Y%f>W*;mgnjTFa)Nj8491d0k;JwcjT3D| z7qK9h&MI+Ss4t7^#IR0&La&s?Y!$nnW`2*30@p14{zlgT=?#ha=94<@MO2E><!V%d zYXAYW4G2_+)bueu7-E)~gS8ARHp|wSTsniHOy&=Z6ct5&s`t-LN$Tthx)`GH==Tpd zDN9GN?`^{SnnZorAOEBh<$y!BmjQKHA8gBaZ0bk8%RxyWC{eS&8~G<CjJ<0@bO9_r zM+zSPnU&WS>&}J2bITXo70~*-GvKC9gu8Z%TTBkKg^A|0Fy8|wW@~<<p7hwjtfQrc zaAR@Pn1K_3+3k*sDG-d_;~F};jM2n?unHK}0yeBoG+tObR+Py@S>)y0ESd34UFRQ` z*g&3m5E~+>^rhy<Sni|8x+;Ok=jrStgv*or!gBAy&RYyC_0tj3pO08lCrz=6R4Ivl z@@6TRQVP@u6*Xkw+L`E{Dg)JV-#+*W(tmAA2Uj*@!$N&mR`+A|JlxzMkctRY>zXo_ zV6=E!3qat}{a8F6x0P86gb?WQy7`ZMykH+b(FtUs8D*I^BtN5lL$SJa1#a+=K0!_; z3=ghbX%R?~zM$$V0uO>P?ZPImGW)yTDn=^>T{e(Cq6`vDz5B$BtTd#%J9zGnV8(Ix zvM_I320r7N+(_S<Hx%Q=#hM!+QiyhX4?Y2_|-SDqZE1n?^cZxag8r0eW-1U9m{) z1gVHxv=yjcmJ?j7?7JxS9HCkSZfzqiNeRdlH7yfivSI_m5B>v6gYoNFDVQXYq7BWq zg0dwe@cbndqu%9_B;I|{&w^P^bk?7@4~pxaio~t1Z>~y{6g65TqexY(dU+dh3&ReH zRvTC`;HvBfhuG?6$f)0MoB$Bw;}|0S{%euiQNm5$9VVE=y4`UB*_yOJs_~k5doKNp zcMC;y$8M0gLs06+{;SaJpHdN7o6pTuR<nbE5ThJ)@jjVe4xTu0OGfu3HgmFBAU)dE zjG3jN29DIx;8fcGBs-LYWq^0U8RyAN52~<Hm-{k!ZsafTRK^cwzq*Zo0y8r!2TOQK z_JiMNRsOxLryL}#K_`8LrkU-SuQd6v12iN0kENg%b<91bgcpWU0#ID)2ySeF=}_6y zmJ&MTeM;RpX^%WxjI;}j!VD)PYMQb&MDL~GX$@!TJb3J9pjQR009in$zv#fuc+ksW z$Je-V=+0BZ#ieR@UpnsqBD@@^UuwsIaQUGQ$xs=vNQRAClQyhX$6KCAM1mt6^d}ax z1bQpC2QFMjJLXscQQiu?weDaWI-|MBMzY74*G<b}s0H1y*%>(CKjqjnXkp_cKlhXh zDi7t3|0N^)0MUYP0AlhJ5e@y1825TxDwipAz4{ID*>QQ(V92`z`h~)L#vP%T_@5|4 z*@X*q78x=u)L6bV<)dG0L%H+2nOha@_4*79C=1JMP#-rkTio@bLs&I7Z4<Z5)lKGD zU<3iY;Bj%noRN#T>6V^dTkG|u5An+xkPl?skQVw=65-s~Y>7P|dROPXO|lK^6&-6u zry1cop>uxy3Vx_m#LSqm9xk9=xh|vA`W8X171FJdBt*T$Hla6`f-E8OCH^s&W9%B7 z&e%i))d3sQ@X!Z^DM1Mn*2r{H&tQV!ZEqcRF7E~PGsC(CPWtw2CNf2u_{nN!V&{O6 zPzoUZY(>)8i=aE@4qjyX+L$D4B*0^^JEGON1OM}n#3bSh3#Fhexp^0{JuMQ#vTqXv z*)BY{wDEUdv)39gMoor=sslp#P=4t?Ipa{S8Hwaf76t_J#Fnwj1;pyb?tlh>xQ#Yg zNOdwC{D|u?Fx?I-zV^ivm?imVDFFI+Mt>$JWp^We`?|M7crQBk|7M%)z9uECjur<7 z>JOGI09t5Sj@3Y>cn&)ax);$bqkxX(02S{`7!*!dkXsm@k9wOW4qHu~n-q^mhpdmm z^T*C*qE{Qx51(Eh3jy9f4m=3y0Lk0FH?G`y<#77q<qVnLT@G?=Dd%$BK(K-$KB!G% zlyq2tan*qR0v3x#6wD(Aq&z*n!_(*rdRz}&A7VL8)&~az(l=>0QTB(rSi;YMAm&LS z-$hi+#X(y)5_KZ7mIU=SPb>x9cYmIRP<Gg{U(?0>MNocjlY{w34wGUA5gHyWUiUvy zfATG4KoA9CNM+>!RnW^pheS)-$y>+7VPOb%KwFQgu4p-S8OkH@(x5nk+MEn$Q9XVm zpTb32E3g0$EG>a0aC)5u4(g01m|2Vtitq~XH7-F~5}0|S$x)0TLpVr?u(ED|aO}v| z4l&(fFISN>(n+0DRyLmfc}w4$+K=4<x$831l?)FDE`A0Hfi-x;0D7$)(6SIEqb&m6 zIPRr{Xwdz~ra;MGGV*o;^5Z6mR~&N@++YISjfa_58K?F{g`Vr9Dz#|%YjL`R1XXiI z6YNs1>@tJL4G`HCR4_V7{G#oV^Fl}PmGXwtd5lo}pSvA`NNV{k66cM>3V-+$lvd;c z?9s~vAMEF@FoiA~#w>=2_2a5OE@!mp4%{s~iFQdtj%Y8?;(mmn!LW77;(y|gAzcF9 zp|f{VW`_hz50~<J;VE}qtfLr9WEJUS;55Ygb>hZNvCk<7R1&pNBT||~55%|r4tg6d ztXy2(T;y}e7$>uein#0-O0+6Qc=~T+tv!o?-I&7s!fVl<woH%i9>{n@|Jp%QJdh#A zm1Q?CdNf-PlhE6<m+#X}Kn(B)?WkhpbvZDUMO|CamgOeZ?ylfU<s(UXh+pbt#1gVq z=!MF3wcVsM7)V?$#sqoG#I)M;7=Qqr)!Bx^9i}`Dihxr>eSMQ@LQ9h+puTQ<s;$Hl zC^3Jqgu`AovZUP_E9{O~SF<b-`$v^nb%Rg@^zWbXzB#Diao%OhNwzlR(LboK$z^bG zff7k*NFCe!D}QNya-D~ypTb^jG7t=k5i>my0=7el+G3I>0mryh#T{*;-D<Q!nqq38 zK5a@ds<LdH{;Z#nG1kvOfZgB9fgvcKu(J!7A)p)4M(ioB$1MyH7RZwf=WN|s@iuuK z3=xcEC#~S3>NxteUvR}DuIMA~=p!y~m)rpSo)}{fRscc6f_Uz|(!s5MuCT9)oBN=% zaS3%|12Lhm&=>bgEp5GTP9FHRWN$u%i3)Zimyl*alqHsp{3FS0Ivqmy%+=@)fFYcX zWKVFE1ZA@>8Smj{1fvY*1Da?I4S<2VEAIXdAI>mBuj*{nKJvyaw#g%l4L=r(R8Tp? zuwM+r&oYr@gYsLJba@>X@9XWv<6dl~0FjSObSV-L%@)E-#J4-Jck~H%|B#eMw%<u6 za$vI^Z8`D*jcZZRDLU@Va)A3#pFwE_lxQ?0CM3c5E>apb!hK5_P+5{nEV|Gnd|BEf zG>vW}Q#J-J>bB0oHj)DeaL(8kZ2rECUPi78vm$6!*yy--fNt2PG0>>B$oOVYTLpWp zqAsd5KhG$A8Rm$7C$#S1^d|4s`wj;`044@Q*li|yOl-P-ueW=bksc1F1NS3(7C0PK zyX5mGxp@f%O%}}d))SMgr`3RM^1iK)+)QPDa#<Ba*gh=S8sZ2OfMU@wh6z!8aS;G) z$SADrtBaJgh>}IoOy`X%5RjW7)UZai3|9QZiTH+$0RaKo2C=t4*h0lWe_~O%K9a^I zv?BKZME|DX2Z#AjK$j0hIMAWhqa9id3|w*I@fg4|e{RpzDX|4fsK6!i55~fxv17qn zo8-tad}RcaojRsAd{{6EQ)db}kQ?_tsh|ISN1Uvho{6u)6P_pKrVp(wEae_1M9wBc z#^P!GI)WtN@WfyV&t^(HbWFR%NiF~-&bo>uz%^sVkVQdrFoP~~91>cxmk9*^G6e}J zI5Ef!i>Rh~00tvkUt{I-yv61No1&j5Q?-vk_K5bmPXUyP$Gi((Nl{O>(BZRW?kH45 ztwMqSiQA<O*)%3eX&92J!nF3`9RlK*k;ph~N)JBL2sa}x0I;e$EqgbcOS|vX8E8$* z(iMn<oUe6<e2hru#~Zx{N?yj6jZs|Azfdl*5|4dAP$lyU>o9j3RCgo2$!BuQlN|Rh z9YP79*ULhYlCivVc-S_$MV88f{|YqfJM0I5vfEZAFToP$#|o*-b<t$exFKO$aJy<V z<i0&is#^VsE4LJ{7!;oPrY3E}5bi(?L80RZBV0{SynEa3Yj_5sPQ){e5^ZyjQm{ZA zEN~z^*ds%Zv!z*{E0%YCvJe@eIuNE->y`2WEp3V*+YI)c`<-CJ;sFb0&tXyTv>eZH zGj15yMqmIILpH;>*t-N;of?K>G|prA4%1?C{X873iDZp$`I5Up2gAX|P-?ikKhaCn zDs0&}3?y!QI4ca8;PvTLEZ;6LE75NFmKtT^LbRX~S4Sh-O3D!d-YmofQ9sv;RWC!X zhB;iBUa7fy%2dAT54KSKT9?!h;dqF{pjaY#{SA8K<J(6c;j}D(3F2je?ydZqjW>_| z188(eqV)szwIr0(MwWyF)-BL6GgzrAX*`d1T@x`ZRQR7T1i2$zutB`uTNou?>ck|^ zvUr(zeiIY2Fq4xk7^`#$h9{gW`xqovSd}ocn~7TFS5T$6gpkl@0DDLBIa{pzsT6u& zu%8_Z#EOL?IR-TaQTdbt*WTk96le|_s#~UilHL`~Rl(5V1$UasB0OxVIDzu<{?fH| z?{P5u#+J6BFw-q!cX-DRL<mjMFrnQz1H$z3@c8gJ;E*uY-z0@(`d7HZ@Sy>T+YL*f zZNFAVGsx0e)HJfY^}s=UMz_oUAT-QRp<6A7173hE(7u8QE9EELCh-;@jf!9fjLxYl zGR#*e^+YW#Ave0r5c{oK{3S;k<Q06P+b=@Xl68cZhT<Iq7*jAc%S#4>uQ*8mT;2Nw z>>%bimut0P_o3=Ul^7U?_@`Q7dYp&k-2vXM-opRHi%}DImh47Nk79^EMff*0p&Jz$ z4mdDuWcAho89){lyj?Hn_3GxYW%&o_QD`|(Y2Z9z;lh2_vFotZ=u-|#3#~DMsftw= z^l?v65-DZufoL^6OKcwP9tAV-!M*XYXW}%X4(E{x)3;V)TRo;Eqav`fRV<x{04u(5 z?%_zott>?jjn*c`hla;n1`1fbLl~lwOaf+dwHf8$@s8jGL*rJOG3yESJHqCH-4biD z+_wY;?})p7Bfo&O#4S14<HIwcLWC;?z9VjixWe|R6d~>BD>do4*oBlET~?>6N64~~ zoRYS71-4H4OHo1i>S5d+f_DWOCNUZD%ej6w`r}&H-MMFp$)~tsFZjZid`lV~PGmWl zGA!`dDPYhsU99y;Y3wjP5V!Q(&6NX!3$f=tQOuAP#1fH)fa-HjxA3DBYI0$*9Uo!D zdpZT=B=Xsog*_l_*PUim8{B=331qCG8C?TK-Gz$90@#otizF#D%s!>LZpnaeUiQW_ zFlX0cOCo0#b}=?%@*KC?ZA;q@2|>+_W9BRjNAQG@Nh;2K6GuZPY?*2p<e?Ny3GoG^ zaO|*2o=Ys)Yos`0>+tyMVFGf`;yryf)G36?PD7&DkbcysFEKFg;E7CPrvijXPTf)( z%H?lhy3r~*cm+5bqs(c~n3fC*P!|ELdG6?>CG0{qJ{JdM?;x@h3ZKv%tWVhDl&{zD zGPtk>Tk9G2=%t{#VT|5uozLgpw+;}@!lNb0s=iu?u5pY1Nf+(`F3v`)Mr0OSg`eS> z+GuGy#qP5PKi?x|uX(2bUO~8D>J%su+XWzkkL4r6CGNe*$Y$4akU=w|gKL%4>B1a@ z0b)wV?SnK<MqvKz7-#zk;Hp<R9=8?Lm|R1NNY|o)U1=sZ*=)IZhJ=M2$qACmBu5w> z01eT5!<7oVLQ;$-xk}XLXIwZLA#uFNq!n-o-JEI<tHxn>$|p9DJ<i1`2LoHjg5zH! zq(#kG4>^Gni9CbzNhL@hLZOG7Tv2kz06VDK#?cs;#TG+#1?Q7|*lPQ&a6O7T^SEWQ zi=FUI1Hp?5A!fqRvPFm-gx?8jzuidI-`a%Rx<t2+f7KL?Un;UeXo?kZP-sq-xQ6aA z#QY%1`J(ATuVLBg!L3UqTQk(~PCp7b(fDkHcM>E6eT5^MmxFcNAi2L`=LVq%bm;bJ zMY@F*HZHTeit$|ShbDonNP~TVEmmfu<AR#vXynP#-L-L#C6_QhwS*(-oRWo5C71&W zMbW__gSAJ9dQJg{W!%I%TvrNwVh+#1hN%-1W3H^QEuq!KK=k1TXvh6vCr=pc%iSRP z+;n~(9wvYM9XSu?fwoF%(jD)d$%@TtAhkOJ#E0lD149=B)*bO}=CYWP{!{};aw-YO z6f}SXbXGFDGqYy$a?3sQbAZum>mgX^SPT(E;Jbry?)gPH@CVXeGH6Fk;HJSOe`PNP z7c+Ut>+`+2D^{R#q$@&6_~dj_|7DEWe(4jQo8QQCP<=mo6MH_X1cBRAzr;Qs;n}H% zSz;xg)IM;c3tqq<uV`GDwdv&`hlxI&6`X~*_)PwwCk}OP>VK32dKEO{Cj?y3r@Sv# z-@znH*q-bKP9Odgu`9%V$6$8@pWyYlP&<CWjM*uiHwqZvg@W9SRrf+TLhdpD(y*7M zp<-74!4Izn23xxH+G1Ubtf48A;V}a7n;6E45}q3C?|Tt)Mt5z_*$uCBzbk8njg}Q^ zY^pce>*!aixbKhZ9e{zC%zAF8fY~jhz@(ZK@g;<>J|a=LF`kMX<Bj-KN{yOj+?fpn z4DLn`BT0;E!8d54k#tt_l!V3{?}Tp@&?qO$47RHmpWjEmNhdHqVe9?A;R926Zr{^! z#kL0tc}Ux_?>Htijm$w8DKKZs%dJ|h9JG%Ox#9@!MTViVBkXHb5iQ7T;PCSJO9tD* z%dvF#A+T**l?-Qwrm5+f$1!gSUr|4lDrPAoeyurO+Gu@j?!n?v+IL6sMnJZm>JiOw zL7Q3|Vq!XjT!Chh!M)Lr{-7i1aOufl4~@<Z0_I)n%vbukU63UUz&%p%_dmAtILPqN z1S&K54q!q*U>sbbC*GL>uc3yPqgTK|9h;*%1RJ2!A{ZfVDF)17;e|HSNM(4S52IBf zU+h-EckE%C?71ri143<JcMK3WO4z#a6jP{7sz3#*<Lsb}qDw6k_FC>~PovtN0z;@x z2L!cQ7fZF=onvkg;*N5_TZ<c-T?2v;j;~%`5y1X2KObsc@{M0^GF^O!2URa7@K4H+ z)q@C8Pf0+M@}N(udK*j)LB{Q%4|Ik&bjSp^a?r#ks|b9!Sqes&=+q{|LXn{LBJOB8 z1XK1*l#Q&`M@hsDT|AWR!gSjBsj<F0FHs%~OJ#B<qUmRa0sjL`sWVi)n?~}q4U`j1 zco=Tf5E|~d@c}3UbMlI<fl^dk<2mr{nkUZ0K+BJicUW6)MxvH5ZQD1xja0(}+*V8i z*O%c;+_^s<`{*(~DLNiZBEh9jtH9Yg%EUCn9S#=sc0Lli#~y~7Zlye9#c5#*a4t#| zaEKYA`^u_$u}tyMe?7tOKx>6zfLEp!a8@_^+N1Y}AL8SUT#@XmN=-Umrv;oK1AjW5 zLENLC8ylE%VksZ^_WtA3A$V)SJN{qtqmLW%uOWh&iLQ2NAA&;hERoXyjw0Paap>>~ zW8&lnqrR2x1Cux)=(5D~qB=R*V!Ktx)F9<NJft1vpiPf1k7Z+nf|a65pI@4!FG$`> z=XejWyMP^ImJ~GD6}ZDUvamfJxVSpC2k;Xr37#-(Mx~<Fz`(FDXjz!u>%?qMQCGk( ze$GABt@;ZN7y8-_GHt8zb=@>%bLlh-zHDqT1LMH@7>GYy(EqJ2VnRc=NWFQS-&n+P z#X|>M_zhPkThjWK%7B9H+ic<oL)qsJf<oQ_w;u*Xzevd@Z|u;Awj@!9DWa;$!T83g z7D~w7)|3fJ09t&MrN=$W0Mn8vUpn$ecTfC8G1J6413ukQ1$Y)~_s-zCS%h5-4f;o^ z*yL2tzm$nzPeXAr@~??l`4?88f+xS^OYo5R$jC~(;tmrx2uu{;60Tl-5<8!-6!y^t z%qbrb7~<2X5D#d0fM=3&%!8*uuVCRpEcL_yknawFT>(1*KFel3{`pUqxotdo!3aJh zOdff~%q%`9c(=$sDC;&HJqC%JtRS9P=-oz&g*-FO$_$xOTjJx#@zrxw6gHgEf|HA1 zraTkF4X{i^zgltk=Pri?V~T+mxdq0CxAuz(FBJV0DGOSB2&2si+B-M@Iw+|CNuy<S z1xpes7DOJn_Y_?60|NrY%*HmH9G0p$L9f`#t^ow6MsBU=NesB6Wfh6hSjHNjb3O+0 z@J5#jgXE41O_SRq7omvkJ4~U&3CyuLw4r7|r1m6Jc78?Dl2v3#vxDiMKlc!je_NLL zAduzj>9SapQqY98j1i7^MduvY5=r@e;1%qc#5)78YM7QabP7ke0kJZ3SFlNV?hRuP zo|<cz7U;j&-nJ59<2_@f^+BLt*-KU$^dK(JU$v<N9Iu&h-zm#asx>p{o-!=7Qiu6@ z^-JCFmZo&(000!L3m>+F7tAz<F@<upJW}=XbWb|Mc}qS&Z8<sj5RJKj89?`$Yv95X zLqe0#G>U&x;0L67o_4F1$al**EP&|@R^X*nlQ)A#90X0kwkqhRx&EI({tk99Kw-q{ zCg*_MJ4jD&jNKb+apa3R$NiVD3%nbSL-YntpK@5#XM(zJIJG5k5nJ2VkydYHNgEb= z+=II3s*oVMKK+Ndrx!rU3lgQ%X@hfc49&c+pK`|s9#FR2+_T)!^+abPel>&%*C0?o zhO6afBcjfHgbYPG02;oRm~}`m$>s7CdLrUbW7t|ihz)nhKA-_CU}PrO$UPxptP$O( z)K%{elJE`~w!n3jZyNK3Cq{mAzpa!>ROo2c51oemlfgw9%K<v1G(qjHvCXH?Zp~yk zh#z<VXbg&xC(p0aA$TCPNE_~>rR6tgEL7JDI-)NYX9TX(MXiJty8)Qf=A9o#qRbM` zMiXtg>j4W0{vrD``vtnQW@;s2m`R*|pj?1UaHzg#9g#reoX~Pr#Q71-rLUALL3Xg3 z3z$U1b{+pp!K+O|4?j2%;00z`Y<RmDHr6En6c=}N@h?mfr^J508yVqTbR9I3&P+U- z?NHt9*;VR{Ab@TK*u9&atqzYV67{bI;<%NFegjW((zBoBC+_Z_Z!GIm3bH4$hfUI; zwc5O)Lb8+3Q|{6vqgYdBk*CA+<`Urd-5mNQ>Xtfg-i$_>D>f}<V`w7vtV1$Cl0<T0 z*HxQXDdX*?nxt(k;(dPkSS54u;j{=fZ31oYtnj8qSeTz>3)`Pcu?L7f4Lssff&f$+ zu!31ajo4xFTF{Fy>~}Cqbb|GrWf-qQVsZ5{oE4iSc#uiw0)`?&hK{zpCEVSx)1sMK zS>a!B?z?35nt8FsKV%rfv$<*9(&H_K{gYLDO4d*o#itBPB2(ZQ>!U_hr<RmPGrNJd z#~{yvo^8L`cTg{9nDQ=+@g9za`%(n}@y<g9mxa+dn?o4EKjN%|g-Ewnf|fR?v+Bts z+|1veJOb|mx=0zPz={no{t$12gWKZ{@Af6@LlVp%%pi-y(Pmemcl{*GL!C0RXc2}; z8#|SWs&FPrP-dEhEnDjNJO{71B}#)eoK+l1I~ML8a9Jx*&p<D@rw<E|)icF&Vy{KV z%^ku8n1EY%z{Em|Pic10RZX>SRr9Rwnr^%l@Rwk6<#gYJn{9f8Mvy|DkTp7Nw&D<$ z${^Pj#u#O}MT@E82ddZdXy!wOEg(XLPYy1!+61ddv*YBqE=)Q#`f|5$+_=sn-a_=O z%>t2QFbAEB#pP^6(_k+u6RE^p;Y*ic`geE)14L{<XRSmjJyrt;oZL4115dLZ>4*$3 zM&08-v<(9<I9MTB_MJf*R;0`^z!!^b2AlobrnLrn^1M4`-RWxn7LEX8T6Qz*bHOpC zp%E?bRZOV1D3}?XFapV-0&Nm5%gj|Fw_-|npn!kBd{KW72^nL)cLC)NXHi!pQ8wEg zu)bDE*qhV6mAdP*3*FKU(AAadD!YF_w}pV35x1~T?x5DtTec(EXuGTYz@~9ao_G2T z3b2T(U4{i9gzt6Ye&n4?j`?9n<*+vKBpCAiDq0K5kv6pU)ExY~I?KP`y8sO~9BE00 zFG|B1u1SgS)tN0e$23`GLU{$Q(m^aTvRK@h*MX@Wt$AMJ*|%<;0^N=rxBpqf1Tu){ z{7M7*l#X$J)o=<}CRDcaLFi*~H{5`gK<(X8j0cmavo8qj3{7!7K8OS=e!}m&VPmuV zPZ-rU*)TAOWI*1yw#8v)+c-Lwvdu%tfEbpD6&cWNFpP3avRaAQlcEz>4&b|>M;X?d z!<e_kJY1J-6Z*#mZN_b*rFdQ5LhY19T8UqSf^rJJFd}AxqjYvKK|l5n(C|rQ@G?Lz zGS?@CM#(BxFb>V3WrRk(F>ZH)3wntak`Q}FFi`1L)>{}u^yCa)DUVyk3JCNiUEIq; zj&Ke-2@LNFaJ>5vb@LWxAswG#iCU^;Z*FUbZU2kkxiKVco3|FMOg$HUmOzY~Ii@|f zX25HaCAMIYq6ao2_|(SeH5QeLfEF$`=6U%*kOZWx0akgg7sx-E^A6>T6%}k66y~nR z4YojYh+#;wQZS7yXuHu3R>MX>m|q|07<_{Zo+xEGcvq3D((qQI`KvWf@8*s)tmq@} zn(?UxTj<rJ9E=SrG`Nk6<1NNs`nXm46-+cOS<PmfP$pXBtMCR^;1<_yyYhI1{jsYI zqw1?5dpf&BVe}5TjF){*;&Y-gOY`Gc$`d_g^l#!Qc)^F|Hb|66)f*PD8~hVaXeppV z?+$A0K})+cH9eL4eRrmjRU&jn6HWGKeMh%rNbGxpmX_0k2TRZhgD!axxesyQsOV`D zGtQ_%X-kX_ij;yWRwfkJtvIL+H*{aOXlXpVt&Tv`BJQNX*GR}+v{q|V%*sG!DKJ?r zpgngkTtNiNR!)!1#5x`@`TG>WTfziAH8|ZDUt#d#Nm4@%ncN9OBsNPp9ZzrzHXua$ zJRC;%Vi~CLYgjTghWU4xL^4tbJ=>uK94N4WD7eD_kT6k0NX>!*e08BCA?MmjI^AL$ ztQ^3|xkKnvR!D_Y!m~Eu!Q6eZ1#pa&0j+*hXSyZJ0oyP$zeZFjo@NJ3)+7PPO%WN0 z)q{T3H{x~%f}wr#ZbCO|H`l<I1L-zOH_mX2utl*}k2Ql37Tj&U2V2I9(iH9hmK^S1 zFhY%1TU;|l*8x2n0qkIFJ6uN@$;R7++aG~5+$aI<=<O$2+ugCmARf?T3xU4_-rXGw zqtem%EkJ~xz0_n-VEW6D?A7EE54MS_)bD@^<v7W})eHO63imuu*fI#o*fWh;y)Yb_ zRWf&Q17En63_P2{l{m=CX1K~J9A*clxvz<Dt+{2mSa!=Puur6^8N0!<Sj}uHlaSn* zu1xPB<M~-JPf&k<*Z)owB*9icub6w#=bx?09x_=VjFeh9&cBWd_ppiD7@F4U*;bhB zA`SM0WoPKB8;Ei)1nS~YLv@wg&<h>BlV(J$c1GG|&C$=62J<c$xEYWOc1M#!Ob9WP zg}9^EbW7ZN{GLlrTP>nG{EJIm?&YG{63#o8O~D0O7=}PuZA`o?P`B15%ETIbP>;%- z5_y3I^>MOwD=d3>wwdDxY>0Ps2F^Rk*wQxW9ybJ0B&6%G&?#S&M~9X45n3jY(#Mq0 zfGxf!L(-E~U4`I{J*~GlO+J}nST1*XkkLc8gQ)IqN5JrpHosILS-20=_oKE*3C70F z_k<0BjPPeRr?_+ICJ?I}R9<)B>~ZC~*-mz8J>rBmd{9XPw_m|omJLhBL6Z<;|C>`7 z;h>cq<+L&&U1O%44J!)2Cr_V!9&0OT1vMqEd$?BC?oH;oWk|&U@US3c*eOT|1%4mX z7L2DHY;2q=w163y(xz8+0Re20cBXFo*i2u_@DcnZ-oTEKVZpj|fP_xzO_eFbxuW8v zz#i@l*GWLR)Z$li++cjzhG=^H)T^PH+amAJQgL6tsMb~tu`wAe1?wYHRugAr<B^Xt zt2Y3qW=$vs?ons%cwwbH#UL>U6Y+3{s|SleZXb>fo0Wql<zPBG5rYDU&H*EcLKo5$ zX?r}qLsp(HvQyh+1Fj5@%{vUJatV(d>?#b1AR@S8&kn|!k!*VleOIa>r>HeDddbGD zGh7#8i}ZM#um)C`0IC}CgC<8m#lXPLgRAf;PWZ86Y|RZQM`L?<x`(B0+a{c6(@5i- zaDv;UhEQonTH5{?Yf`gsavZC%zKyQo#_3%9DVIHn*{6JTcaek85{8i*#GPi|sV|cA zrsJMUHehzf6|i>+6{TP&->6)vVMF$%8tqPv6Czhljs%AG8Cf%xG3f*9idj|N(#th# zx4e}~P>PiPI}O#tCz>n)CYOoz-Y{OtjGr=4i%<uGLh$%3T*}=U(4(y~R;Pc|A@r}# z><U&e*x^QV=b|Sm1xi0+{0G-D6>SAgH4B5}X~L6Y$#``K#7|fb7Bb)(gN~a(iCu)2 z>W&64vQw-pw$aE{J=Y0M@OU)1IP*Y`0efxQEYK*$?bYx*`!awY9}MVVCq5Yr-NQI$ zVfL9*lGl>Zfu}4MJIM)xjA!dx?NDauFC;jPZi7rCcn7CbsF{Jm*lC2!TL&;>8D07( zm#4Vk8yGb`EOIFDXxdd#f9alG2|!fuCSZIA5OcwC(vh5ra)6|ED4RW#2ZFLEfJ)}x z0YCE8tQtPmw54~+BL_)t4JI6VhH(a*#@bY(r7;&lShn1^H9z27jRm6{5Dj4<=4)5@ zT=;;43Yw9)KiMk#!!fSt3cR9cL52AOg9bdAW>F>0qA>sqf9d65xlv|>D9kvBy@pRr z+aPSz6&B)^(pJ1arOTobUb=sjgU40QrK3Fum57qYo`UT_ljk`k6QN-bxJFzj%uN~W zTWU-gE+%B-w?Vt|c_$+gn1&c&*VP_A-Ms(Me+lZDhbEb3dJyJJhtbi8MElhb$6z{y z_}m8Gm>`DTzzv9)5}{h4R!k?GC?k>Ftm#LUoY~=d5Z32U>Gg3VYgt^87lvzk_)&&K zXKZGN&;(E8m=X!e*<eF9`tC!wfV(w>LchTY|6bhlD;z&}tcC03a1G%Tk&E3>@fo5= zVaE=45|=&>xS~1n7`h7%3zb@Zm{KwFf{YI)I69%URb1*ta9A}rYr;bOEEO4H;CSMt zXqJpb?;3^}pZ9MmsG>q7bclJmr3lP+$H^&Pm`5xIhg$CrwsZ$u64#at2n@j;H1Fa% z2CsgDh;Rc_A-vkCUR*&?9;U2$jE@BF*3}(Q4WZaU3V-)mv=l4|k!h36CKtdr-=)k{ zf-c<ifi=Xka%a?s8Kyf7K_0LnBu$_|VKtG$c6Or_Vh<qU6a2jffRdWoi~c%fN}bXb zXrFp+3sgMgg(bh>jC;+X!`>J&xB?@Y0QYfkHl6``+>*qR1SX`lV|?TK$#X@S2(&z% zG4cR#tF5?KB^$CH>!QJ2SYT~~J!M8zNORfc3=2oIFlP*tjl(tN0B&Lpz}EK{HBu^Q zSwcm)C9?yJst|t-43xu8uKh!l0f!E&gl9LJT9XG_+$7}untsWrk3C_z$b-)<P)MH) z*7dPCo%i-29FKL8IGr19<p4$6H%(b`trk9S%)%>j{3U1uWg;RC)<+CfF&9(kS9{Pl z8;~73i@3shi(hJxlr1WBBq;|Ic1U2mH-d9goLsUr;O@JyG0G_}ZO`c)z!#aOT<=Vc z)^MHl9foEb=VtU!<PEwWOFjZg>^0G~b;Txa4p0)UK%XPfU;r>nNGWK5P&^bYe*gvW zkyeTP%mqZaOXBI<VK0(w3viJR`G8wTZ*P3e!%sLWMFH{7Bp~`+F0rr4QXaY_Y@u@~ zVVq&h#7rf~3L7<=S8w48?}F~Y2x7)jsmzh2t!G9#U|MdEnUI#a2jK1EeB6-1S{bMS z33;CpE>S_VVP}xZ8|_|?pV$RM%%4lBnG-q!F9k!ogRF9-uUMf=>rg+XK`jY}cLt9S z2W;?si+~M+4+spiSrKt}KJPT4m82V0y}PG<y{!qMd83xaG80>k4I}&6ih+9<*K=8^ zT%uVp8U7QPVhVEYcUW-If(9+1Sw0Jqgci%yh(%;sP&(5dYvjnzsM7`Qb{i$<rlSQN z6&D<)(e|OIDGm0F`8w0=aLF^n#O_)rPpzNmB4n%mgtJBo+kV{HPW;6-1jEh`{DMxv zX+52`X%M(oqvblu8LP$QzL}*-4a<teKJN-VE@wp^hTBxk$jPALT_b+Ln^)#ILLdPz zE_?jVr8G=5|9$;%4-%r6&UMm67F=-oC**Z)<vC{GKpIM%J#_tv{FEwM6dUEN9CVP^ zQ36iopP}$H+vE{5UCSU$PNDj)usyn5O9NhgZfH=q1jHUAIXN~5-Sr0;5K82wxMHfr zgWINAB&A|qg95^`yWojQl5SW-+WBVpKfHv@WOO%p2G=E7A=uxHiE_|muhF*n8_)`g zshy+T_Z5;?xG<ufGLvBsRynN{7{M)q<ZUGm^UU5NH>2YRR1vk;jSLZ&Wd&8nF1iJe zU5@!X?%6JMPweeso5m}#VWv5_BM2;XUd$jmdFtAx@0KP4b7al%a;M{S2?ED5vGU0= zE?M+DWlK<Al4$Df*oqGeIZIbX^Da+ROSkK_+umw~&Oj-k$K9ky_Y_oXMS|sdV8gI+ z)9)s<H12_Q3MM{n!tE<C{Z8{UeSq&svBPMdmixHsu)r*(Cr`fpAx^@mkK6^xslo+y zLQ{7EyKS-C&&@;(C%xN?tkdSS-0Ef3ApD9)8Iqo~r7Q5MAxSwPkO`NksnK>k`Gcik z$ro@>Sy)T?KVS}C5kkk(t^m#vO}q$d4MJGOJF#&I6l`^A)n`iQ^<gPPO@w8Dbq6ij z|1FD~5mk6|O2XO_58WN)$*Id|P9E6iSI#?=?&-xAiwO$EJtcofaL1A)`#b5yRwa80 zcI>^1CHnxLkl|Mjtlus8oR+96Dr4MP*OX!NO3-%%5&76J_yP>O&<<S9vt^Jg$wQd< z>vm7;vsJH?RMIC{5k<%i@wapZ*eRQjUlvCVFd9ye&zt%u;WERD;^-FM4xyX8(L@RT z_VsWZ!U;A*6F>qGQg<t)6;LsEUrfo$1#K6FP}a{!N^ZgU#>KMAr3aUaS)Ar`U@IPX zP_z{E=mxIP^!33AmXMFD4hZY#(joXlG2^Wy6v9Pf`}L5npo<S(yCX3@`>vjz%@*_o z^M+sr?p;{LP7YR|71Q~KX0jA9)URZ@6E9dFNh64rY00nvDA6TM4eVtyg1j7H`{W2H zqGTy%Xoxq!LfV#z6F^kUKL6WB9~<pPlin0Y8srX}WtJv(lK93&BCpDXd===MHoRqv z-N?eRhZc-3%!O0>6vN`pT%sOX$D8y#Cre^D0)f_FpS3Op(cK|5EJR8TQ7$1vW+Aw* zQvM@CbZIFNLK8_&-W?35Cfc*WFwrct^~DVy!xOQU<{GmOQROu$2Sbz)U~0Bs=}6#r z)*g<vKTx<9NcXY@Ri5tA{p|@C6V#=B&D_G@A|n%dDlTYl>S9!Hha%5U)WqcQCkSO? zr!w)=6{rEa6QD4apX&qiHOA+}GEut%Z5y-<%-C3OSQEn9<Ox?I@u#cw*UdratguPi zMXYWH6s+DNY;P4jgr%(;JJ?v?wI)W113PpotNej&KHKj>OFsDSQlhjnl!={c-V|oM z)qD$##Z3DS&O`e)>@C#Ny8IOvUQt;rj35g94J#mz-LM##MjbgLif5`+aXg#2T0zHK zSh9JTDXj*wh@bqj$7D5Spk8RvH<z(cG9D$73tGrSQcwROLjrOY9bU{HANn|ER(OTt zDm~aiLw^tEGL4&@H)_rkPf7i>2WQ(jB%#ok@g?Xr5HZBCFd)$)x7@uRbGR1uA>{x7 zUMF1|VE9R7_}PII)EwPmD`;wZOzpMH1|~U-_{6qxekh6Q4+G=&fesr>LBb!>N0vnX zJ+ug|$&jF@-n)V--N1<G%Tof1hhv+M#lq*)4FYYM*s9wNhDc$=O2*m#a8KLJ?D6op zj(bUq7<bKaZZLr_tonFYTJey^Cwx+?P!rh!An9aM^M%f_;V!v4ri=!$6SET?Q^b~f zj7a@l7{dM6<ff^L+R*7lXiF<@<dof4-s~X>1;gqniO%a#7{|OMYM2yg(xVMBGrE!a z2EE7SgbW&9rv81@n=^W91&ruY3UUvsl!81Y!E;7(hf7|Q;0MeM9E=|3c)#QZKAg^J zNY9K@yJ)jK+$-mFkH>x@zjz4rk`g@@)*a*ks&+C_`$j~ohFe75vHR*>3}?}9bky2f zAX_{Ch=$f(H0R)jrhe0pYKZ|VO2JprT*J!Ly94DEd=r-|yMiVg#v+@95<Jn0Rm>c6 z+6TLW>eRy+473ysx3alQf$8AhQ~&t!_aOo8b_IKzu@*$fYmr4GdDI7Mosz$lC5Q#v zN5P|GSSaqZgfSnvfV4>_J9&Y7UmJ8iL;8PC>dhzt2I|XJpZ>WGM}C3mv%p&Q<c^9k zg@f5A@fRM^Hh9=#ja(jj4A%@7{%Rs1GZuxdICmBrv*ZZ+3>w8jc5JEml!RpnQwYzv z1_lE;2LtXSa|@P_#jD}5UBn9;_n#r;V?c!L2HKWK;YLz)I=qSbVsX1d!Z)}}*PgcH zu(`OK*21=p2%f*Fy(7T8q(04+u@1nAT#{DeB$FA_0Tcch)$<J9ZMf+FaTi-6z{DY` zbYAdz)IouArPeeZNzKdp%|7&_KgG`f4qku4-ybEQeEru_IJ7oWjjG)ED+h%3p(Bp) z(LJTW?k}e$J0)SlNw1reZEO&Rz=tzr1ndxs2y!c!%{w9Puweh$vIKPU^A$vdhK|ai zM9>=>1H|iwXL}8e^}?Q~<@kr5WHOymWJ!esgt#%Hx32)A2HjbU==JGuuzU`9Je;Sh z&A4a}@(i$w?5UCh{#X;+{K_0R^z0ROEk84)O5Fx<F6hk+-rl|Eyk#{D<ON=6qmdU7 zbdRp1DlrBI$dRZ~BA-nuiaFNe=W=J15PH4ljbDx>dzq1!0Xnq9hZ}TH2RA3>Na&us z87#f4g(F^AHFAdtqh+WB6qh_#EQ?Gp?o8|SAzrBs*wT5;@Q!W#ItRe4)-suxCB+O# zAG^l8-3$<~)GK`P@fFLJwpMT76SXz<CF1Z}{DjeY3yV`aIKlATh6Fel9k<CGH+|b6 z=*FN#FsB=5f(x1|9>qLBPC2MFwXOlFvlR4#a<F5gz)T*W%mCDwsyB<6as;ilCow<( zen28zp-O(iL7nEQ)%Zu}?!V9xRO4M3jEK&$LKE1at4s?fZ?s>mEfk5!mN_GBub9qh z&5`jRvMZ=P;+3nQeT;d}*sctWu^r`+@W6=^%Eh%?7OarmEag0-w+#GPo8bmU3tHsa z=tXAIMW@o7XOU#5E>a2~szfc|qC)5i?8RG>eL$bx9j=SD$tB--*>lOR8vN?ifKVZJ zoLZ_#@UETSo=WEe7Q-^Hx{`5|qW1@-d&Ke6a#rHw0K&vo>Pw&+Hgrs8?>l01vO%r( z>+pZVKIgm})Sq+h+rdZ4D+&|sQH`*XuXvMKDs~wb%16AQgTump=R-k^pfpKWPG~DS zAZDKSpcrW>0Cad-Oe+Ou3|OQb)T=vq*dn;|h>H})r!&A}(Z#vwcaN-oGn$TA;t-7b z6Xne?SGPklhM5rs9lXRdS?HP)rtk>`Jhx44`nvNyPo^a5BeK3%i!i-e{Tt2UYWnRn zGvhb26wno~q7~HzBCSsgo-vI=Xue}E0S(av*a>IJHhb&E4=%u;4(D*tq!(7=XA!?m zm|$Q-lYkbWT^%l9bp#7y($Ut43y<UQTymr&Q7AbGE~F_E0H<`xQpl%R{ZOw%B@HrZ zohej=YcRY!u-<-Q`&vNWIrf$N_|+Z!z;Ofmd(c_HsNraG;DRvGi{%kCBHpMfM&C|( zX!rGRn4cG5M@?ET4Okc}G=hw2uHF@-o1I+h$FijSHb-|0lgXg8@tGW?6kOZebm_my zK|xnQe4zdW^N?5p0Gd}Yb{@sM0{fU~hr1k9pqaUm4{AhUwi?#S+8JcygBtk1R#Yx- zu}?Xu66dbDD_b@T8`lZT0e2d0haJ*_CJxi7U3{=$@SwESBru``1GR&Ie3Fz~5CpeH zSD%X$vT74X(RYiE2n^}`$c}7U&mq8U#`!z;P^O3|0HPq2qNQi)k!hCE{LnM>eN&Fi z<h(nO@@XMD{}4zH%~xr`0HOvvDk%jy_I!!7U*7SiP!;pl!(wr!b8(FpqO=d_pTzJ0 zUIp+&vGm5d!i}XQ4`4aQ2c??Bdh2ov%~$;4Lz@~CF5vC1q7%hVIBqF5%n*JU6VpeK zi*YYDym5(REnw&MO(K{`Tk|ro#nUi-Xh%tOO;s?2O(8@2Fp{+l0_e>>);7mk4jdGm z_9xZ}Iv_M)1sz>M@!`ORg^Z6@CLXH|$#KQlG|q59YoaQuMZzb{zVC5^n8iREM!L6P zzh9iTC7ia5txDPg9-#1Gy<ofa>~}i4a~em7fys<)V^G=}(M7tw=>TUK+?2`@B^%^^ zpws~#(vkolch6I|q9q~g;5|0;d$(ocL_bn9m{LPRov;k};k41iKXz^0GIj=&yT&&& z?1<aR<LgL6aE^F87~L_hr*J0%Z`eA78A;^ssk5D<1VL{4!~@A`6+xZBhUiNcR=#ds z;*z5l66j1>*0d|NG@~c>EX9fKM<OeAiXaN&bvK;N@$OX6OWgZpAh0GIMCN&-#g-iB z)u(nT*NDO(iIUiU@u4YwXoyYB(2MX2)*o^uS|R<SBmNbSk^||X6cikT=jIbM*&=Ki z5bD(taEoB2Ah8tiVPEu2QO6HfK#6sI{A34w{0d~yFe(duSCef*mc+&pR?6Xj0zJxT z;#p3DQ^8qjVVJl|7=oKAQxC<%G}W2uE)en9;Alj5#gf4}z(jI}ri!bzT77V+ket6J zu}VKHC`bn<*ti!D3R@7OfKV&uKG=<Yd*q`n>1ivL;HSizS|Mp<B9tQ+bo7M*1ox+` zelGc3H5fo^i8V<Hhcx&46QT<Y5gv!UzS{hn!v-mlXAU2jMcZU!^*(MS<7<ZPFpqTy z)~i}MK*ppi_9i^K(-LF?lE#VOyJ%|C3O1sSyJug_np)5ewc(2ZGvQUXvQ>9OIl>#p zPVr@XROKP$V>db5A9th18k;_b24FN()a{g`6&$|~dji7>5YK%L2~C!XU9f`UCta6} z55H(NL(4%6N}l5gU9f}c`L(A0&M3wsNq10$$daThsIgV(CX{(@g)`Jd4IRRSgAerx zZhwOdZ*Dkavsw*I8x+y8wV*z-+S_Tjla0Q<V9J;oDntO2OOk`#R1?y+3PKB)iM_$= zV}hJe;hM0jh~=_muNE6)CkCn(p1ZKPfwN?Xxx?{K9TMFJFtc@cLbHNJhJ$VzSB*00 z9m2$EozaGRD*2|CcFJyi#U-v5o&>xzFoJ>P245!nSTY0tamfh)%VEyXre*5(dU$Bq zq+A-Ba#9g#uIby_B}NCZS8myFW?*z@|3)*_<jCLbx?$orqAenIEzy9ei-d81B#iB2 zdj9=<(a>zh64lE=m+qhi^@`%|47_sC#mKcu^C##=?K327IpI1BOkxFF0EFx%A#Wz$ z<WLV0OK`0MQ*Rt*2Y@?Dcy*t0uz~^a14ELC_YDPW18S9!e(EDXu!5<9jEG?Z7In<e z-K32B&WTQP;--yczVq}KNOEm>!l$_y4HF)-NS`ifqOSP;M!XDrjsUenomb7Vi3GXH zl1rBK_t4`1Sl?=ZRAkwTZ@Jwv&#zKOU<DrUj_qWF9}Jrx^JgPdd0(DnF*LNWMoJ}Y z9dnZdNE2QM625Ld=x>EHYekx#h#U9`b>@4@s)2U=xchgEyBk+>(XPO`HygwzL^?35 ziLZxCOQzf5($?g07P5!2fS1((dNb~$j<~W*agRIO1Ck9@_ea85?-*4NPW$GvGI&vJ z(r#YTkj`LT8x><9Zsv{FpO7R~!3??#3BzFkLWTCZm4zpOgM{=O-8ybDL!2@wG*L5F zl!BVYyqa-jWGPeQ)x>sNB(WCnP8)5uoVfHSDFH=7TbT_92w+YCQxif3elxzoCJ)LR z(U$iLaXWF!36b(?Il0m_Nd==GvRjnYNd2q;D7<Y)d}?_8XSCr2rP6)#I%H%+|2(Dc zm|7o0yN0ku5+sX_B_t<g_9*NQz|QQ9W9~?^^zI<!pN{hF=FS0D4gdmYH%R2jUIj$x zA4l*hTLltJK^s4mfDKrb>8F)%;|TurGILe)Q7zW@_Tto=C`y_Iu_gXOlcx(vq$GpY z3IEs%Vmey~0xxTXyoRDvrfg@lsj%RF;<Xr`&Y;~v>laQiuk!hXin)knu?O=|?2*Ip ztjKb)!u;H~i&L1%S_&qP|Go&woC%)j2&z2#H89*|pv$sxGyA)J$Ok*j05?-mX{;kg z8cY^8O%rK}02tgmwRm8jy_G%m)*jTj5bHpNySqCtH%B>r>jVtKl)jC8fV!Nk^SD<7 z@(@mQ(}T^NDbwPR(BY`w2rni^nNv#73?_`Z-cp3BnGww9;AvFg=jcynjY^-IY!G_H zJZLmA+}16=jHcFnq1+CshP2VOOhwi+L!8y=#_1+IGh7j_xDU1Yj4UF;ACI$<7x4Nv z#Fv}dUAVz-z=n*Pb&j7ui-Ku`p30y**DYC!e4#~PRvB(vA#0_XYQ{T2Tcl=6tHVs~ zDR#)vravLi;=nXNxIy{ta-^c=N``~KfK#BuDrG>}h)PP%jBFcO#0e{8NarKP$y2CC z>G~WS1U>o{ok7Fb0${M=poUJNk)@b%b}A^~a1rrXwgr-UVYu8zcR<0*5_uz28xIE- z_0%;?G-_UAmZ;vT6-wtdBhsBs&49h@5MFf#&wyN}3h@7&mqKa42tzY8z-E#GVDpE3 z14N}@ZT>`%Vhai5;uMP21eFi9;DHx;+gts(8&O9TPGeMg0$^Fi7e!JWGj<u9IoD0a z9W#7WmnJEbinKo2E1A~xBb>h+NGS4{cZ?9>#B4k+V+WnPe-|UB93+IvQARc??{OI0 z*{Y9=qUh?J`cEtv@sB+gWrsl`jqlD#%gdK*Dz;W^xyXxc2r(^KZF!XSCXyqk*5jGC zD4N^Hzu?YPd;%xfA0>1Du$8lHQdTS&&(PFhiR_Hz&toS*hxY<ep@Am`gDwMt4hqTt zvA5?<Jj#qSOi3`Xf|jD??mJ+i%-4eM>ZBtbrY<RpfRcNU;1!H=madI*&{l*-Bgs87 zL>5AQJ4mH%O5hO|0ofeY&+ss(OCRm*bkLv&M7KDhhfM`jgoEi%u!VAjsvojbuGoJb zcFVGwQ6pK|uFx9EmYkPiC2V^@oR!nNPotT+{W?2?CPK?RWArT*GLsFQwc*8@-(i6v zY3e1^r9)WIG&yUTeJZ1L&?z{betoX$B=KafYVM+<+AgTH5`bU1)-Z>kbVZ{l&Q%Vg zf|*Qh2vcZ?zto25K~0hxmOkA9z!>gNdyYtq8JT#NHN&APqiQ+N;KRYV!qr2c@ciB| zE-3*jUdU2&Yv2YR4Bcpe*rY4yqDANjFJKAau;5rhtQh*I9I#VJ@{nnr>lsrLt;i{+ zu~4JiF%$uATgc7{P$KkiPplazZ={O`hJ}n<h~L;*4yu=fFg@^j7`=Rk1=1*S$_w{} zkVIDc6?9Uqirk<&xJXu0&TV3&P^T^?MI8&)Eixqb?MMuBpVWrFWvnOz1-YY#c117y zWzx>V4#}fq$+Byr6d_T$kxl~}!a@3-c*KB*WQVechtQhBBc9v?j(h8vt7Lh}Ra(lO z5ER<{{-MmMH^J}&=TJa}r3-nrulUpq61T8)IV{vzGR`46tw{!i3X7Xs;(Fl3Spgki zP$BNYoxeWvMG7o}&FiEJj<P-xOX7rfEn(F*Spk<TJ4H(YVZ<zagGNgV^kB!-V1C5P z91^~=q$I<_R@kt7x`K9m>3-7`kwU=Kwc~%{-biYNgmp}-aWC4Srg;$Ma?SeVjv0<A z4xuK3r^U36vD|V&%YmWgXJVKL^o7=-k}nLP%DqurbeDC5JXw?QD$uj;i4hj(s^iAx zrmP5?$dmpj1%M^`6GCOWjcjDf^tk!gm<3FbJ9`Yy%Y=hrqb!7{%197peK4G*?U;2M zUIQ-&ECuZ&D6HNcJccDU+|(JzOrxDFOrR33J)hb5i91~qu0V;*Ci)F`FzO2K1a#~? zZTZBlg6!nWad8Iy*<+ih7)&Od;v!u7nZk~vVk6m-$jYldyI!Oma4sH{E=mw`&B8!z zpM>RfB~?f%7#~dWD`i~2#mpsPkaXaHZPfU=osC!ynlKNubO_6+tv56Iix<=nj$oaG zpMC>jBm$-7C<O&ZGn`Z>6neRQx`QO92lVP@^1{^}K;XXzphVW52}-~yG84nEt^h-8 zkWExrzBWK<JA-00aLaJN6B&s52-i*E0d~KpaLVperi|$HN`)md!++fuX2x{dx47xy zB{)#oF?oY2N=H>gUZ(9oYmlC*unjBd;;{I9JgT9g6{kHIBwEa#4QI40G6H+Y9`z+~ z+jYjVxi?DwJftj`LE?x|C%=@3G`qMiPV5qR_6I_xac_m^|HN&hR&iWJ20YSYZQH`m zV2TrchZPdFZxx(23STH;V_l^e6?YlrT3Cx|)0LG%Ce8sLXe1qLTKk>Q{``(tlKICw zf%)4(oeM@Yp>2|(0?T=KO7G%rSfv!)bff29DSS(U4hjHxK!?BJg`Q5Kp*zUTRncx> zhdUZ=<y<#P4*HJF6~tJh6bzJuA!e$KYlEpy!7#mfOtP1QCtyMxzMbG}nBZ(QWqh$| zK3qyODw}!+d9yP^>()ldY-R<m$cV~DSKADlM?Rz)uX5E*oBmeBNE05izDz9Kl2CFr z;99H0LHY!=lz71{S*!&sw_<rXSAZ<^hFMiui8DTK1TWgAQCUKaXm0p?35KwQwz%lB zD5L~e-fbxd<Trv_u_3vkPqR7pz~FB((wzSbSC}<CQ+t;ULKwq6Zbd<Q413qg=~I1{ zXOO~1^NaB8GOXcU@ctAdCMxrMP-wqnQ+}5M-6B+>7>&y@J;WcIFKi0$c3K{daa))y zm}VZlKvjx4*oFZhq>EP&oxl(*p-VY%D<rhut|Ur=8)RAQ(yf$BMyB^w>bxYd#|a2n zw9_rEVOyK4bO%m6E@6P!Z8KnQ028w?9n%@AzQdv&+~Ow$K3G3BED9UANu6%UJ-k!< z44qGbm%H6-?6zrRNx3hsif23b?JYET2Xq&w62A@tYwU+mtG{D$mL*{P^e0tn#SeVh z=*W74<mes;gC@o>tq>X-*TVG#%T3I#GLUE#zar7YiGp21!OFZXJvmuu{NrKMRqVhD z2$vOn`^{i58Vh9!5Xj!UoVV3+g0QED<8hXnNw&5~Noc9e@8x=UWVjd=+yX`pI)vK| z(1L!*kT9ei{K5*Rh!qrDEK&~ACz-u#DZrL70nBY(U_|W<+8-x9b3jNR9YX!ojV|Rt zw+&V%9`{Cd+oKuFF~%L%Cc05j=6Cg`@CiemEVlhgY>|fOnDF9)8bGDda}f`~DKNEr z_P}VP2Ow8t6a@Srfc}UI3x!Zss$2IsHbW>zJT{Fs5$cktOr*unG#n}NzDwMT1}z*h z&Q!Pr9MgjZAfJggBO?5Kf#?gxEq6YKE@YCiW!cVuG<!%G(IHvg*|BJZ?ZD$Cp3Qsi z6fP3Gg(;EERni$6N&#};ZJKD|nn1l@QGbsek%${(?wG)oUcSYKE`P%8yyW0ady;g* zx<<k%Y(S_IthfT%^7OFclrs}IY{6n}2z@;7Aa!xw91>qwgl|w}P)M#+GzTz;4YLda zYK}NQALk`%emru@S(s?Ij!;4L2;LdYU*$kF2ueOqA7G&!$J)CbM7Qv~pD?EV;uW~N zAZ7I>bfX<|bC7(VNcSX`NZcTc_sI%(b@#f*VVr*1H;HE3VZ8@;G!rslli%eqZiXoo zb_Mi=O+vU9HcTr)xjJ*ea4((5HYOX0-+-klEKZrS+%}M|?)PL5Av~_~%cch61vVsr zgzS4S{>pTdXt7oBJ0Lyl$`lm@_|#*9t&G+0gq)fh!cz)7o*<-+Rsx#u2gAZ-<If}k zS-0?bP`IZQ5@;xq29(F0_3VL+6+rh1XI)s1!N_Z3`{W61@i@IqRhd#|2J4LaP-}#2 z5H03yg)4HW*H^H}I!5+tk{eq*5AlX!JTsJeS9dTd`K&*GLx)3B*1lnjHx23Wb>`PO zy}+k#cG)Iu*(8Ko@wtzE{RJl2*9RW+^RY)MuxSAkR^@803phiUupA4=POD(LadNfL zk{BK&Su%oiFtYfkCP0Y@%eK*kRtwX8*DPcnISCuIqe^!$%*ZFQt)|G4>?N?J4mr}; zelR_NvctP<lCoG3*poO6S!ZY(1dRG^h7Tz98HL>P4ND4=tS5pbwrO_kZdZd#(BY@V zJvt&opFk&In!?mDW`s<b&c5Zq?dZ2~4+d?-wpgVVtX?)c0>uonl#1>w=8z-h6#l3j z@kt)cGDNr)5H@gTuU#WmuJ@q(c8u{@2-*L>aL-;6hPCkS)FoL6RGWdr#n*uwYiyfr zvoKK#er#ZLX=5@#%DN%$MA02wT|sOXp5GD7T|yP=hifqZ7!rO(ED8UJBA`AnER0K& zU?+W$cw9fcqxx|`(;>mMqt+oz?Fy#X1!Ldt2*&o|V1>C1vsP$FV75O)Y}Ka_55#qo zqJ`rW29rDr$`142LtBO2FgkpNYp|6ks70el&ZZ5Q*4uNQsYF2GL&jOCQ;f@w0O>9O zF&}q2BMEzqa05>*LVg$8i476=fyy=v&EDB=v^bfNK^sNBhK4qSFxRlT6bFunu(>Zq z8ffALc1Q(-0le6JS3KAqR2mTNSS>h^fPKz}IZ^5og;*SBgdC|~CMvFY$UHx?odl=Y z5kS-+xVAQ#%5$I==n76%+xo*K6w9)!YJ`BX3FKAV=!nK7u_|{2%FNG}761Y>-&%EI zh7Ssk8<bCXFep;apU{LZxhYc&-llA4HEm4|5-%K<{TkYc-eSpkTC5?;qsCHDKG;C{ zlmiX~+7+-IOm+p!+lP2zXDk#w-Vo=@%RzyFZp_HIE;D?&-5HHx3oXK1-9L4JjGduP z87uNtdpHNW_IkqLE4PR{%nL3?TsAvBVmis%&JZAGwCk?ThlB~awCDgf>=b%+Vy!tE zK_InpT_o%ahl$Ft-$5w$La_QF?BhBaT?|yZK!*60;I;I))j}C!XkbjsNNIriL+5LU z<;j&@wIJsAEc7KF%iUdzLh(Ff+i;xXHt^M6-N-up+NDga887JELP#E1u)4C@Pzz^{ z4GPKNl)a~$1D>h|T;*|Qp>whXaH*IFi;`E^B}|u|8#y6EhE~uOHSndWC8h}zjf}kv zOlHj_31$6?6^3LttNoE`6$S|#_0}%Yp_La_WjZ@M1H}tEOF<KQQT@P&`NKwx5@4S% zmVyy~k&~1H3d4kD=h-4WXN*LdU2xbQj+?Gq>H;55iDT;M9xIlEpYFh2^*bqw;;@(S zwKBk?9<;{-+)l?uUKf=ia<(fxdX){`AcpM6G}IVDI#cRl@6#xoHgX2>wevmBu$%Vo z9C)k{=x^9tSTyFjnbW`9bDX<7vY3Ih=Q06K=Lkpd2DJsIO4GNrR>(}3v5JQ|-C1Tc zzUHN%#BczK-(&9#OozCYZ8-!koqkg-^ynRKmEHHnppH00iV9WB#WS`CzP<q=X2_PR zrz4SQ+;HPkMe~q#cy^yU*Xmc&IGOpECBup&Q}!)c6HmB5r<ZJJ9CQ6xC8v*Id49Tv zAvPzS8p-;RG$v7S0y}ws_))teirF~^$7%n&5%{tTwJT7#aQLZzj)}F`h~*tZv|ckh z1@8`ML07=&rk-GS4AAPE+CK2%h>s`Zs{m(fsI62u4^HIRK05i<6nZYL-_2n)BPY}# z5j!=pG5=shT4ODcJwiA&nGvsE<O7%fv`TK|W2maI$FTE5vz3WbknNqxhY18JK?jR_ zSh)-qUHzoEPrR_Tglv&J3>QKH4xv#~1|h16Wup~p>_*)?s)>zh6EBo1*$7y!0^5Gb znU8;nA2zXU?K^BJn3czHEOZ9Q*2HzPx*OZs7_vN!#V-FMlRHw{BOzB;FI}{v=xdvV zV!2MgxSc&{oI|9pK!hh?!L(gL<0?j?fYB95@Zx23kR@Mb$$&6~!JOC?s41fOp15_g zfHcQJ9o!5^lrSLtwIt;6&w>+tzb86_=Q!4$hqercAH>~1eLj{*VmmLaYH3%F!P?7# z2LB2>b<kxz15<LAGSu&&|DHoQ=xoK%^@;C8kAJL?+#qv^h$RK>sQ2iyJBaHG?4VSk zh#y-bAnCk<-34_A16bt56%ET*>TQqHetF+YdbFSPDCoQ1p=e-L<D>72Jv#1FkF)2T zkF>(bxqw~Qwz&PG({41gs+Kil<)FstBtzMm%XCcJ;)jww!SRW}@FeL+5~sx6H`Pv3 z9JO2+wS}q*P1``UR9{%zu)7q=5}g;z)&Zf&@h?(OcdWw;_F{d;9@$<KvNzC=Fnlv_ ztbM88?4A&1f1WPVeL~e-!eiP?L%S2${L#NOP8EX<;dvG*2PvFsPV1RqcsAG-=te*Z z$6c6)L%0R}{;dOo;)vgG?lB}dl&Awk1JGSl6kSGq>?!2{xfwnzj94xXi<*5r6}T~@ z7tS+Y3cxCCFlCw@w&Mf$orY1RfrqphC;M?g9wP{q6Oz~kiS_a5CLv)~=j_?>$3G?j zu?Apg44ZHhKg@sQ9$7&?OT|-5EQiRDbnG+y1zyqP!k_tx&Ek~R0trt8??jFL4_(AN zlV8{susrr9@pTXwy3BEWJW-09C_Z78mLz|bxTE}IX2vrgZzW)H8I;AU;he;dU;_+0 zfyj--mY>^%&KtT9O~NtYI>u|OEF2r-yA!H?8-Y6>d|CqZGOTZ!?*0G)<cLXu4f_&D z`SK|Z^+Qt>x*}BJ&rL_%pS)&5iY3ayF^(*@RoI0agz=*b0A&*X?gr+k9IR6ko)`+| z=ZA&yf#b8hD6i5$=6?~3P7aJm!&C)3NaFf613nz|aA2f+LqKO~PJlZtM}_*VVLFA@ zlmU$<0&4U>frz1H2}Z_Ap4ddXrUl&mPLJ+}d&|IeGDp~$4on*~W}RFOW2b(&TMKCU zu`of-!NS~kenqVJywA<i-{Qr7uB%h^ISd-SR=+^cGt3ZLMbAbqMWv6_c25|e*x}A< zC%THRsl;j~Xo>s7im9P7=HPIC5cHCZ{wJI3g&mCll7gU6igyNX=FGV{ZG>(Vv6#p^ zI)NgmnE*_cve7tXHjcD47Gq2t_fXdcY9f<funeeS-|@I;|57)99o5+cwLcuzt0uE+ zyU^l)^hIM6$~XeB=3*+}cR|>F(-_BIkG`OT=nOtxK^2@aUn*xeJ@cz|cwF&wNk=FE zqOxdHtAf|1sY&AYjlvVtLXAapqjxT-M0MXz;j4u4^~+~7-c1e~+Jbzsej-&HZd?^h zMO+&0S!aXujPw#};3haJJTGTJz*3rXAO-%XMif(kSd&wNXTD5=<8`$V8B9I{JeOC! zgSkCIvWu*M$yIW*uW>4X-2wX$6IFtAEC-MV&ncy$h6(BJ<SBI_A@pD=WAEjLL^LyY z`Se0PS)3eN04pFekrr(q@gKRkf%NDM?9pLE<llK%TKS~5qif=8*aqyO=yKgeX;v`z zsZrI)VSnm|#vY%#MYFVod{C~fV?c}YcZDYT3MhDIppk6#V?@hj9F&9AXlOQ34z?U~ z)g34rxZ~sdZw_pDzBoj*P%bHSgtxA{WcpUO$@-g7=8Xb6bO?)?5IG$A+i;&$Ge|s> zLdNh|MYLeI1=}o8hJrg_2MZ)w3y*ab#&}dL<22S7rb*-(I|wWt&)8gBnFMPgBaRL1 z9SW)m^3$X=C%gIT+tXOJ&jou5*ij^~bsPNb3J4mmnh<ct(qKuW`k*$Y2<fW|RlEmE zS~CC#=3!7}MG*%jT3B=1I<L#X5Q|5Pl=XncFE^>X=W?9r<~RXY6%V~l?6HInoj#dN zATE&6%$)SpvAM3|@$#z_aL?6+oGjSFSvq&vr*+91<(}NVlmP5@I7GJ+ilSP@@z_hj zl5J!ElnHfM*RDYIj+dZe$Z~Os@Pp}daI|g9SnhTc-$=7$Nl*-7MY;GsqATbU(z>q* z_b3ZyulD17{3NSh&o%F%B<p|?dxxt!^R8FK{GIM#5H=(;c{rBn<)8o}+*!&276QwZ z^hS0XBLM8$ygN8}{c3Uju!~;w!UT8cgvY%mTtp(;LJBPVd#t`&^^|shc5`})qvfh> ztSc8>oi=yG8E(iuQl$wUx<gtbd9vJHKoyoUX%clvtdQj6om^U)qx2NK@tWgqsJTpo zhLT-6gbbe2<)lcaZuv+a%cn!=9u95XEl*=4#z-Q<7DpCvAH6}Q*~&4#!y1!rXL*Ms zoWHeEPkmGC2y&w{b`r-7hOYo;8cA+$Mrg6@gyuFUP$m){9O8u5<>)I#QM-i2c=vA} zQ~s!$o7f+1*(x-=0mok3$Zh8&zt;1){1?lBh2x*&`>;cN0vE_q3idD0BxN@TX4`qO zY334{95ryZGjL~|C3>LHQEt#Je9FX-J;2mA3odEQ;aNZh+Je!yjqb@;3T_P2(OQfw zi)fskS;V3NEcbIs4=d8=jC6aa3=PG(>44^Uautb5m?l{6hm3%BVu6r9F?n`#M7C#H zV>occtD`HZS%R%n__kB5OA)4b2}wRu;Lo$rSQbbi?GdI&N$3DgnmdmP6!hLa7SCG^ z69h7q!Nc%=pGnHZ7|~j|2b+7jR}2j;_<ltci^r-l%^<kB)lrqD>nEkX$^tfC*n+o^ zDcTgVbK06n!C^f?QeOpOu5^_nRlx!jE7%fL**{?m3z2P?!lC^U>pni5gDknM)_R#G zMYE93(*eA(_ve_WIR3gT^m4!sVzLy}Kg0|5W0qJiPm~B0=(_6FcLn<!*VBT4yO0D* zn5ZJ-ELICMWQ=FU0LC(CiLPN9L&||oq>tf?C_sJ6EQnl)%|?*;TaOH<LKkSDpr*Y~ z4&s_XiUm63Xdo}~PzSoZ`aEPwMIs>`G`8)9hbhfWdz}P;iSFu&S$9WCAYxNdKQ2O8 z`;>#ijpUA2f@h=WD1po$>gXB<Hu+&f8?nlsbLOYIdv;0;4(t}HGn*gQ2m6clbITni z*hv-71!@4`Ek<^l3_`27fi=f`_eD)&WimXY^>GG=^Rbpl)zBGKF+#`~BzaG2v~#KA zP~@hFEC*lVf|i8BuAtwhsxD~Qze5$&Gc|XI5iRmQfzP*50!}$-cqAzUQUdN$F#ijt z^4BP_{ZctgjcqZudGgN=dvRZ1fpxc&S9zm!9xJ5waYLI@7Q_yzc9a8{pEb_P!<cm; zWrPH}lNHp1;>v4<eN0CQIVfn$K84#j#sOdlKF|h`K+ey}qnKeM1|*^`w^Z}<vCO0H zu*Nd^1zEiR-KL`j0ORE#MKeqak`uv7Xcl0P^n{A6$GhY$1lBQysx!*BDE;ifV~6S< zc{A?;uc8#(I6@a;!!RR=2oNO0zee!L@eUfFBOo3_W(H`!QT96Qppf{!xXS{TdRtb# z3UY$qv;diMA=J^QwHX3;UOy$e1cR4*4N`64f^bGDppC~B%s4MElqXUwC4Ip&oQre^ zQ~Xd;<i%TdRg1LkK4EOIq!hFpLpVdhzQ~1w6?CB^>gMq;ceLmttfRhMcvN4G8(QDN z(U~affQ#sz^Tf2k2=^%q=oNlA4INZQ@ZOkiX7#wy-7YbWx|gHKkNSueUPg3TUvOE> zJBG$$`*Jf;L6f`<2<M*EnY$Cr#PdT&2eruR!)YBlh&;6I*tUVPL&|PoIt1`Ik;|Ee zMVcA_xa?nx{z|CBv4$WwX-fYlqEkA?F>IcAWLsb_vrXXJN2_|r4!SUxbEp?3!VOC2 ze#2pTg`=RWT6<VSIjY_y?5TiD&BV{zKGaQAJ$H($fzMivoi{0ALDSVhgLa}i`54T~ z2+h7-v0e_C)7;|EbxU`kUBYts@VO$~y=J68B#-OH4P3%h8AeH=Shr64<sY3pf0qs+ z|A8M|Jf-o6nwg)spF5{D%XYz_L{ZI1t#xiaK->=M(Ih*yOF2l;WmMn-=lEAiXq+Ga z6HFGldAK_KPlW6=xh%ta(*?;Z=ESN-0Q6q&P42Jg7$-T9_}>=M^6GRO19m&~fY9!k zJ!1h^8f!c^I57V@V+67PueznJVw%G7en)v=-&%D#(|vp4581JWBvp|pxla|cD{doV zB2R14L`yxmiELph4cvg6h8PmMGZY^uB={|~-brJ}A&#onjPuk2k2@1O2?-g%KfUYg zkg&xTuLWUMLI7t#sPg-j5`o#w8XYiOt&F_K*XclcnqfkNZAD~I9zi-jJuH*h7|^KL zK_zUc*rUX_JBFa??Oo(ciRH1&N*8N}3GKlt0UaN!@DG+O8N2Y5u5VDPk*b}O1i8kd z@yWY?xb)@cR*hs2r-@BAu(xfu5W7xFiWl|j4A$p!LWvdSz--##e?ps6D2OH;z|_~h z;%44v43t=m*d4H={iIVKl@AFZxcfHbfX--0W6dy#JAH)cX&O1jHY?Sa?nkFUYqc6w zqsRH3&Y>pIO^lJIt25B@EGD%v7e|4)_7%j}9FUl9vg%t&Q}wPenx*cMpv+V{(H8VC zh4p}0A(3*XJd-R2kvuykHgF9cc%v5?Ve|rA)<qjjh9oXt>ut#g8<WQ~tbI#>7j=OV z(^CwWZiqJmhipCEB+PO_s+RA}`PAu~x;{)qF9qd8NfZe2QbK=aOGQcnG&zROK*X@1 zWIknJq9l|*n2FvM?9XF<!W8m{V{pf}J?2VNLQcaA%;PYdr%|~1(C9&O*@ecu7#`0x zGDNu0?+#%k!48`EpNuV$?sN%2#c#yba2u_VA-St-=e-!9Lfc_XO#14sA)Bs(+xH4% zR^wYgmX89v1#$`!2I;&7pNCHYMN+*v?db8l9qBX(zz!z~S$}gbc5I=M3z}qnYqE}5 z!WiS2vVycx=nF~eh-<2o_)SeQOxcpxq!>a$)(o9}8QDhf)Rv_afK~GGyvUN@o3TTu zRWji8b6=rG!-c2$wmYo?FE?tMFhRvQi)&*6Hl-fB?NB;1mE7Dor?-{nU5*WbV+Tuy zgDvHtt5>RS52OoF5rbQ<?~e^qR#&to5<S>h7Sk>nZt#|ZEihug$jQ%d4iX=+Sx}CF z`6+dI6gt`FBa;YIE{(QOSKk@w9%Hyg-xI`hZfo1eO}*Z5qL%}-X=mxYp~54nxJf+9 zR8C)x2-VF!as&3*@N{_%u)2};zDu0)o?^g0ZVDB<j3jZ?B}s76_8?00F+d9Jr0TnR zv3f4>Kw&{bhD82Uiqn#OUqdtOPhYlgl~s-tMw2x}iE?m27)pP6hFsmd<F+|4jjbXQ z$P$SRPK)v2z$th^isZtUC4Q>}{?-a3mS&i&Cb&N{fRezASN;B>V@=FE>W*E)M((@= z3ybGALorW{SMwlO5E3wXIaP*zZE2!Jte*`+r7K#O<Ng&bG~pzrk9Bf$M}_A~WK0Bb z!9#lg*G;(aeJpea>p}=06(lJ4C>V(O(+%_p%sxh}Cn^-KMAfJvo_nX!pt*^xyoWO- zHrS6bQn%QvSC@uI){1(d&{>`30Abm7v`OB9uDT<isdFOF!P<2fqr$VZg45Ur0H+%3 zD42!VYO_a;A+h~H?hA@38jJeQ7@j8?w+4@)>h77!>HP`LKo+TZy480~o^~h^&)xxt zs(Xnl%;gqk!1d>9Q&GHAc%loCE~yluaQ2Al_DD>Kx<Md(f{-ANiW=R<%e+dyaqHLK z{c#y^5m&{e7Eo0}C5Bk?4S8W(4H~DnQY&7YCfOO}T2SrWH)OCK;<!E>Y_X@#yZ#XC zf%%D49aJXnP&0cODEDCC8m9e67-MEvFh5ep7W<s{cKWKC&lX_@lz2{h)2?8C7|Oa; zND|&UV66s?<m|8-sBp!b^cP-%%d4K05uwZ{rP+*ehPhgxHg-mfbry}~C&C$vz=`M_ z8o67!1P_TOcnxZz4IR_!qDR0L3(4aXj%ajQY_jiT*oN_Na${OU3kQMD+bG?g(*M7% zb4#-0$YJPzt)iEZPiE%+H#TvT0EsGjJ`^5%ELrNl#o_}vfD7>qgs=2ug>t7<um(b7 z<nNtm{{Mp>5zA}|qm0V|H|C}nNe<90hlTL&tlSxd1Ny|5;ECcx0YaeFDBS5{Vjlt+ z-SPZ9NloaXNDhi3gI${%+y#(_=v5>#;8hXeRlEVl{;Wl6CVEJR@PwA4w|^Dg_m7G- z^P(`DuM3NaDg<=m&p1c4q29<qX$Z^xD~pC4)f=Bma(+mfo1`?(Hzw&nb6SVS*sT{J zF*Ko-z*%$z)ntXZzR-j3_29iK42bpiWrQ~ZfHK_&Yv`G(esrKc2M5(0RD8GY62=fu zU?bK1wPm@y-9eE^_bNk!`<pzGA#g}&{JUQi?iX_9ma8&#fHNb$%@W1MZ?B?Uda5m} zH#UCW^m~tQL?<F3yMf8pu^+%Y<{VrjODZm@BI^`b4(uLB`Ii_G{v~WHrbIo&48X9b zZv)(Qji}Sb66f=J!K<D?*2GB@AOA7sVPp5qQ%#pxw+78{L^fb?yLSvu-VlOZ6o)gt z$Q8PJiC}-5qwX&{>&H@0-eHO6C)=$d9L$);ETvcjH)5M2yDBs?wCn`?%5@K;a<~m` z74IPvY1W}604D5eAHeDqJCc3mM{lixuu@kpV(9Fqzw1|H7XsC88JOQvFh071f00N& z7!bz}2+#1m3}@DjnLB~Y*fQ~u%EC%<{*fHaZTXm)j5OCLnr+Hu0RamU!2~DcEd>$K z$-EQ@kPX3k2O_7Agru`YpE6kOo4iN3Y!dO#3pnH-e>cwo;CoHVb0&CKcuB5<Brezm z***&$I3=*8SQ|T~fivA$f&I-**UqVnj-!5(%j{Ez<`GMW5L=ujPBZ%)<Odd*VR~w5 zMPtV_aFD+Z9-Pk$e$i8drRvyZQq%*jz6FGUG7x*UC9AWAUBPG>wlTH@Z@~FJaIP_o zcdt?~1P>L1(Ufjzyrkcel*_|3H>6iOGEN4951~#1uIhPkGq6am!`NctKI-$@ndDh| zLLnlUW{bw+e5q*T9sT&SkdJVK+%Mv$EvaEhSc*+nVX-2Vi(Cu3o9Py^(>p^3*C)@5 zRDk%9@mwX~gDP=$C<#x@xbcQNummGvAan;yhJ^MS99#f4QIMOfTzLAvt;4|y6H7WD zYlQ87jSOV+4YG0pFk?$Tbnd~j%1B15*p@fMa8c3FKvo1|-AQ^Nw3rK`e9-i_f@9c; zdL2MMb!cSca@)m3w^L5vYwY`<;5)`(P(MK|vWu*eNz^lr>PgV5$TV9`FXXH#Po%vu zE(zg5n344!^kdo@tFjD~=5u6YzF_$bs3>3|1Bd3Hn)}iv`Dyi|^L-D`rKO<e#pI>8 zu$%+A5@8+T2^^q&3<xE~D$~Rl>WZ@ao#Ta})W9~n((GVG7EJ0QA=p()1WTu6SG6i@ z!$`8MqTnWndPYy;YQ%aK?|xOFZK-OQLi<lvL0%2e6)c?hy5EhRlXJV}l~^b~AF+39 z(yL98&zI2qIntBuXG9$?D3$T~X)XPtOdp8-L7|a>0iE~>eE<Ba7}>*Pry})ghHDfv zKg3VVIHg{avd@}fiWbA*Fl(sm{Fn&v{e6#gh?x3-EeqeZ0sVnt7g?N<`FBGG3qE}v z92mX<dr7=2Z-VS&ZaK)cfFI5Vz>qpfH#ISQDT_m_kGHiD%y=JfLsT4vkVI!EIVfia zQu)bYzyz6AGmoj5h>};DbLq;vE3(LOK0y)jj*(cb9f7TpFm{*RD5tk)vz{<~HGzf5 zs|YP^Wlqn+^>P-;{tgoZhbDFjU|-J6MDV?Si6P+$1@}g~5ZGwT@(^t5CW4I-92p6$ zvG<S=fP?iZ2T4QYVextE1kT?(T%qJ}aI>wDkZXuAsH6mR>6SSh^qPS+v*p(&vRapD z;7m!jJ?_6DkvGVH$+PH1q-n@r8J8$15p9&OQq8`6x9D9o#H8>~)+kB`;>ik?p>3&T zke+c8HE62i(O}jyng4a*@dmeVV$zrkYD45A6bzb+xd|kIRRqsik5eV{y?iY~!6aW~ z{G#{JD?QN-koOXc2N8qCTTspr0<UtVE~(dg)97LVns{Z-_4X<Nkp2X`;Iwc!=SRCs zLVN$WBtS*;WTa!4m^NJRZ&Ubt<PXrmCaL5FNn^hn`}-eukg~o5`IJir-5toy)LKMX zKd}ojg!@h+Ut3CEhZ|x%XdXlL*?8F>_%ez~7eFI7bz4C5?IL`siE`1IkUNA2!&pqT z5iyC?cjnCRV}E*V2_N4DP%CgGRN<YtmN9qJ&AY?RW4<EmMdp6Fb&W2bFC1Z4&dbqB zxzl-N4;v$#peH6BzxFN%6Qm<A1so!oxwG;zy<PO>FZ3bwV@F5bFaGTcDmXY5i>8Sk zeWl<>>;el$hv*szD{lVux^vlJTST)*Eq-O9Fupe2Q)8p?+yRNm*D=%KJuusFS5k(U zn3t%7HhVTo6|sqU0jb9O3;*gECR+>p<iOYb5x<C}4p^0)!3Me{(i_VMs><|6llXV0 zFf@r^y2`r#`9FGkng&wWehZkzIQJ>hLhkE-?#KDyWBvQ<a&4P0du#4gNR#6!oYwo> z46hShB!BEN3o>7aYSa$;FV^h7-)}^}cELd%d19B~TtJ8p=I%*y^nCy+|7QEJzYdZ1 zPQ=-;m)~;Ga#!H;p($}}^*>1J{*!L4H8}H1#hxvq?D+))StPoGndQKGB#_;P$O1xZ z`RIp`)`WGBk2gDnS~I8<iS7^EgN$hd7HPrm*id(kyQB_Ys%tY`ViezhF<H{&k92`Z zC}^PUZ`cb4f05r~UA@($?JB~;5&U#5js=^pRWghc0L90vd>0lFSeKx}xr@df0hPMH zDt3dbteYQ`7@J*YHglY7?B|MH92(81<+D#zl0r=#WMfdJBw7XvY0nQtwF(bqzG9X9 zS?Tg}+q!aJlGrF7Ct{Q*!drg`sRdei<d9x{Y{^n^Yed7x-~1<3AWd4AUbluP<M;LR zUBQN=Id%mnnSKL$a>5e{yKcyrETI{m5<IDaH2hi=39}(}p3EtR42HR<2lh8$vr9Pl zyC$V(Bf@N3X0UoW=%OmrwDu}O<AT+p_prY|_M`5X$V!PIpO(286V35F+20k?^H$6D zD)#mwwheS8bZr~Tss>iIcLnpE$rJ-5v`FD_PUdB_8b)4$Za+CJaCbjYoPS^NKf~z( zVxI%?pnpz)39B&Bb-ev5$l(jWgSk=JaEOmLfPo#fW|(`jEMQm~_>XXhZSGkKmsRTk z!jF|##yUioR75RwJ{X`H>dfaQ7L8*(ri!s-x)3IFC=e+q-Eej9lTC1x1KQIV%2LGY zXyLYba=*AlpR@!<jO5B2Cse_iT^JbpmBl_WHQX{#@zJlCg-0st%!4OAOJ(O9AS8>1 zmo!!G69h$SOt?lI*yJ3Vni~wc5y{3wD*t{&21Kq|g$c8dXt!>wFe7F-OG3@8QNTi? z92i=5Bg=i8Jo-8)wEuvdb^rP;?Byz7=<KLtzt-gkx+H=jCZ_a>t#CDHEh>W3H<V+1 z$Id2cj@W8(Y>VT3!5?S9yugw$s88xSICNLRy9^1>4=(=~EE7AUD(M3E8G<^hGhGc& zdF}D}@cZDwrRjD(BLiB1*5h1w-#vhPQF;^$zN2~Qm+J`83O^HdC-R5u!iMEU#p@%+ z{83VZc&l5_Ppaw(xGJk#l5e&g;+y=0U?;8QEr*2~nv4*XHlKGqYo=y7IQ4aA$P|Va zaqnqwG^ZF*T<Z&kA@GBl8NL&@E<^rkl!RYvp=X9WL120~XnyaHEpI}1OWZ#u)G<#i z7bfCgIB`a_drNlCSTXkACwovR$m3YZCCcY6IOIo{Ad1|zJn@RyE6H$_>>0>BhAxs^ zsl9T8ez#kLBn|Bj)+<}(Wj`W-XZTo$3&O}QIxb5^QnXq!4h@a4)o|^3c?`uAgru8n zppj;=K2ibS0XN>viYO4Mk0A<n#=DcVT;2}neFCUS65(@eX~Y;3Xn^^rf_z0Mo}pA! zJn?%1tu|Jgp}1?S(uXi!X~E;v;GY~Yz%b1uJAwgmLX|u(%!i9He?9q!%d6g#Ak`(u zGptF6us>ch#x1xcMwt)&mrQnSUHo4yqpWH6zzE}C*v;I;_8xEJkXHWPlE)H!vlOsZ zD3XId#RN2SU1V5v-wt8jS@Frqp}SP(SB&?Pa?m5Qm>ojA9dSDphX|!0Gtc3Cfej1} z)L=lYz+2cu=0NfchhSUkyITmI!IZK9pc#b;Nr`L1<;FW{p#?42HSHewAIJU#B(Vzd zxPEBrH9ZK+FS_?T`5LY}t!lieup<D|!;qP0?52F>3`+&3sE}156Y(%<4Ex6r*rJCe zL-m618nnP;<avRmfbzuq&mdzgkYfElJ7D^GjUD#)o_p}Pv)^wWBB&EFDE>vpNbAF* z3iCll7%kR_{`?dybqCZ{Q2yU@l`%mYaw~3vd(Y1cWUMslhOP~I>(H~MrLoJ@HIv|@ zn4|z&cb0<sR`N$pQIR%-SbDBkd9Jv~aRbdNN&!BpvE3?B3bGu`Tnl6=IB#B3EpkmT z!D`pEwEeoO3zz&ofVVDmUl-o`urWal-!Z<rgBd2=+@}9Vm8*}HK$iabwrZSvlm^t` zOlDuv87IGC{u`XeZXujUE7XZNCKe&d%hi_M!ft8OJuR-cw-(<*J;9$g7#99XXV-E- z0kUsH?|09)<xD8-!2)`fgTYeJ6SHkK_beB4T0v=Q%6dWfgeTVN5+?<=6$@YUj?;gU zc0{F2d18?rf{6?X*OrDg$sO~JT;-FeI#ENVe4=GUbB9l58x@$U`01JTZ6gaZr;rG5 z;pb>l2?OX7`AECP87e*#4Pl);j#S^pR!DyRyMu6vAn}A~lHCDLy<veg5rrvml@Zkn zJKzx&&TsDyuy>39Py~^z+=6c+PDNsagta81wd@cs;-)OwBkFFEaa=74X`Kfocm{8W z@RKYW(gzn-%Ip%N984q=VmSDZt@^{jk&Bx!63_7F3usDe>m#|bBPrM2RS*-0(o(_| zo8~jM#J&dpH9!!udyE5O|FEZ_^4L^+@7PcJeFp(x_d~Uq*F68@hyCT@vH91p7u1vR z6X2X}EgyAqVJ4xO9AOsv{)J}PdWx}Q<`XhpLyqP_*F7vV(E5bo<)!SWNdcVHmqc;p zu+R`rIS@3Tslgg9zh2hgL@?keKuBUglFG;DquJYu$xUBpn}I?dE*gUgh}6x|k_>L> zkdE3<up;RNzldLTU7lwcTX{#{MZCs4*M?(+hbd=(1&UGJb&6z&n2;2UI2XAx#H89M z1sf-a>D?20zWD;<!EDLCV1veS7}+)j{$THALhYG@uH_)+#zVZeCU$aqXjmw03=9<m z#KJCtD@9;B^19}i`Rmc2D486T;>SVzMX`1TScXRIHl(_5!IsRAIhoa8P0F!Yu$LX_ zE#qXgU4~~TcFwSv!Zyc`?}jdD{=+$;Q_<IlNGhld8Dv~tbO5w??nv@<J^_)ZO-vq@ z^t=<R;@kN4B1ou%c$9+~nS5%r?pGf>XG{x$$O_LA?2XYaP=mlIgXW);3wYm5)h=SI z=)(ROqVMA!nO4Weq{8_kW`rLh42S1U3P408mCasH_<p58W8+X{xGq#E?y7%gd>?s2 zi|{yS8GILWNpoeE<jZqVBoCm8(SJg17{Yx^%2((N*cA3;#PBw{8Wj$(QP9a8y(@9b zOUBtI>=TOy;K_9%mkFv~hTFNG_n<)k%H1X}T$+f<GC1CUf4$uS6w8Y`r6%QoN5|#z zyp-cvfJ5ut;@lLM2qBVT+Zs4mP0TSogw!n-q#_CJL##?Il$cL~BEO4;9%RWpLs)pf z{DK<`hY3iRFK=(TJpB{4I6wTuf=hyXvRzOb{HT*%0Zr?MjuGL5p_p1Dh6O)=8&CsF zWO|Jk0!T>6f@!$Q8PkSoGcj<3Dt1Wsf<VZ@5Tx=Dlrl{4zHo5qs{c-08=DHMmMVv~ z-d^j#2OkEJ^gPi`0yJ5@B+hSkAqhfju?>DeCM1mvO27ZY0c6KY9#zpO#$MsRch-vN z!rBw@TCE48;T1(3*W4u>KYEy`TA+rU=iV+EB7NpPeKLiO7A;O&9U&6DB&?GL=A$U( zEwZCQq^Bk9fVdkD4FNyUz><}?u@QLfBA)~Da3+=th5=$ocQ8e^cqNMiU37i#*1-L! zd=uP1vY_U|&1pD8)P(|_^Z$!`5^lsPoC28Ie~WHaX6G`&M6A%Id|G5-=jC>!*vV@I zYRj7VBd$*g*kNh(YeEID01D(_-;hvbB0Xc5uv&^J7lSv96FP383;HC$i_@LBv*#b6 zLs!><f?0=tb#8PAV8o0rCBURCAb;i7<pHovrPmxw6&`}ZX*Go6V@buHHK<t6iwscF zNmKO^IDwgh1%qmrip~2P-Qc)$YM6X+V^`0_F@qDn;SEr03{=<N=(Gf{R$g2E)NuDF zg_3bO&k*Sesps>uUxhY_snVL0$0uFNfiXUHGd;S?ZfQj#Qc%&(P8r2%FNE@woxqj` zSJlb_gNV!AeDOm)51nm7kn$ws+f2#<Ws5Q}oZlDro*DS86f6=Xo$&I7UFPd5yd`jz zP1OezIgrB}<zNZ?eGBhozq1q_XjY^)3@HoTNe;ohoR1)RN{x-WF|bZ=f1bjosAT&D zGNMzu``C{4&wxmuA!&y6LO$g-1%d~g7NA~FEDk~vF(tqV;D-tZrV^3+0WqvWvRKi2 z&xJl8IH)m%l{%zBgg*o9R4jFIW|%OFy-t}scLPCUY49WPn%@P*AG;LFCg9FAOc<O# z1|A7~Zl2IjOr$oFB@|N^?tzd<_;--<y^YG&AUJRE3|CL5uh>|4=hV^ow5)O@w)h1> z<K3qym)D08RAi$*Qd)DxlN;9qg8-r8zd#D~!sh|MeIlh(oQzx*-w!N_X0t2_RS%3+ z$^q7^zjtx&6pNf0hL{*ShK7k@0qd~^pZ*VO?WYFaD=zWz3`sDY4z`Zu_8p>J9}sAQ zs(ABJd=SmemSf|Qy+OD&?L$WI;!r$dO*&#x($4DESW6a6NWFcc@&luZmC-)PWLIx` z2y!hMCO8cH0xbhf<14u;+CW(t)`tFrv4?0aE8a!Z``pmrE6E6+<jg~lpEM&yErS;N z-z$|Z(8>p)>JiI^GcE*yaRaRkUz8OI$%3nBq%47eI~R2WNl6Qq0iG#t2Z`xpb-*#G zb6uFwV-aWh>`W+#fRlD)5~#PX$-ygQy4>piM2aYP1zycpXa3Jgo#?UQNDH~5#Y29@ zSlRwwEfPx(SgR*S(UiNeUa#a`g7LVAB(lN&ia`N5+#;67ElCUevq~fr1R1RH8a?C; zPoXI`IXrW}bdcCL;cu$1a`5=6;VW6B=a0Iag>qmQFnaxP4YD*3w}oiG_b9@C(>i%5 zYEuB{dT5MNumYm_-anRv9Ylw)g#NUnbUrUpSI{G{z4(>J)1=wag!U2M1jn2XDCek_ z3)@yt=c=JREIO=GfcvEIz;Z}f@VNb*gld|@LHwO9i{QlJ!aC3KY#a;=^f1lO?f@(U zl!nL052Dox9dfDS{oRa}i~or5#@leZ;PQF7|CK6;a^T|QDJCHmFLb;zY^Jx^cDCJX zrATALezI_xH5}==)O9J<&ZI`TW4r*BRJ=47-X?^`!l3yCs~})i6e1V7Bv9dONnotW zUV2E7kJxrPy(=cG*@SXbC5df>*5L70VTmev%1Ug#LRd11ZHTJ3QqOiWae#PrvX_Ic z%M0Z4$KiGd`~G-*Vi=-L8gg}9a+n7%h&e^2Mq~JdZ>Q1?;;n<rj!^0NA`4uJa3KAU zQ$L!dBOSqc^G`yjG=iUlS4NM*>n#8AQE_kR4Kyp^NL)&T(|IAU*pz66r+ea+x+s<2 zDKlBTNHW*lU<s$if?ea^@{-W}N%w&HB0*{gcBF172Zk>!9=1bd7{WGK`R9@Rm0Jn` zP#pm3w1RTrUMI(xD7vxaEfu%*+?Kbz&WE?D`MB?}UMwIwWND2>tV6riaE0X;lau*i z(y2Z-_?kgmenoLkK&X*Ue>;PN`{=0gU-!qsLDt^BuMk;2;WHpUEi_1ifTcyci`6ko zB_+_Db%;hbTvzIn_3Fu21(C_yommcf!}dF~oXe*}SRqeL$Y?gpAb^>i86^Ll^0c@# zO>_qxKMr+D11AO_0MrJZ62&41M-B}Ab_p(Gq3`id4%kos`76Y~YnI(XlmnF>87W3x zmI&p8G0By3QjUh~4j5xsdeAt1r-T5KtG_t7DAZ3T>y99`Cv``-0rURF(Vuqvc;Qc3 z9d4%b(Wronbd&<ieH;7M+j--o(D5|?rVP(o#Y%_`F2BHomPVvkvW{4NWv)EFJm6^o zpw$NT_M*6W2(yi>!xcxACviUt8d8!Vmj4ZwqzW^krF!ZK$2Sp3R)eSIJqYwjOrSfg zuNbKA7j}9}?iV%e&;|sfTyRhSBiuKn`CZ*hLAyU5pB4y6L1lmC;z@Z3IYrNlLio@J zPcu7X>kKlqibcc(BxDa*mU@(f=dDbxu>sGW!IVy63mI$$=qnHLq$dVolx%Vu-Kj${ ziTQS$(v#YxjdGv@1XV1L{}>pYF6fu*Yjx#-fa&4Sw=^M-C|eF9RgkAs2ZSj&<RSyY z`4}148PxCuJ_K-u*0Ung!C>O%Jao_nFn=}j5@2ui&cJb$uCZr?PLp8uDcOlQD#B0s z<YJ-aa^bcR{oPK~BFXItRPSUj->QvG@W3W?<a_qM(DoAaIR0IOnK`<GC<j}RNFy=< zy>v<(S2WCn{+%J6@{pa|e#?5dp&VG+VUf`1WT#iFeG<Wr&@UIY?95o%7_RJK;b4$G zZ_B{ny2Igpzu*BnK&QDJ9LRUp6S71~zdI6bf^V`CW5T@xgI+7Q<&;n{XLjk)=tw!> z=LOe=5F?K-LA}2&jTz4-S_58Gf^lHNKZ(xzWLz5=!-GH0`|xO<_~Fd|+al1=P^BF7 zu=e@0(?8{)+#TG*so~yqejn{$NAQOo${$$WjimitcO=iPu`AH?LX)<kOIM`b0e=*g zaq{DhFWM~y{G)A@0@9E7JCpTCV)+e2_s_hMfnKx|;FDeCf-q%fq#-xd$lMvM91a+Y zyztrJ*9x8X{gitg^Tuq)uo@^Uf3Hsdq|s0gG9X)Ho1aK%WLG%xS>PqEV8>MONp7iZ zAjj?DMLvdUdYUBLn-(s<w0wmG&(mn|7gw>k5;xFD(>RIkr~Q-D*Xd+0(;n^?NB^uq zZ$4V4)RTR}RnY>@<(icLPWEF{@5U&Bp_~#L+JI0QR>vo<uFd4KL`DPg3th?q8lD&w zOiRJr(4S?YOLBXcvM}Sw_#^@e9dqmyr+^c8#<;zFEdJ5(7Sz^TlN9@dE&Bt*DzSbN z$$#vi`!Efk7*5O4>~Eti__jE;1U!UfF2a*)<^2&Kl%&DMU{J)|-NM)O<aM^>&Y-zz z!>b#yCPm7@Ns9XK7h#;z{g;<dT=zpXPpAnQF`QQFCGNBd+nTjZt~BPYw)9@dzOiC= zfc-&tC)Sg1dh8P?q>2UaoWCV!JM5EsO&QV^sLvfO0!I%o2UUQH6>MGnZuvH>H24o? z3AfxERP5i-djA26P71KE0-9pPP)BBc#~pS-Mp4g3M6400c>sc#G!6O=|NeRD^c`5d zG~7*raO9!wD!OYpGx=YdDXALu9pe@iP}3UdRSCR01WpDb&$fhg-7^jT9pjvM2=6ih zBog6yq)VvzVtWk{HGB%6QZQs#=$~>hWq%;vCRZ=x5^X9`w753<I<boOpi1s&#FJt1 z7GcMDFaO5cKd#6ogr!@uRhE>3f5H(ofTbrlHp+prQVItIJ;kilsm+aJV&aj>Gsy0s z-3}rDI0a0Y32iXB{~<Aw!{RD=Sh~@}8x$6+Vr1A!VOdj31C|c=YqkByeRdZ9>|?&4 z;sN39z`tDiSXibHjE$~9%K(qeHYJB?WqL`&i;E*mus%X{S_O->B<vU8?&>E8!t$L5 zL*S}{BAeg^NRXr8hta96B`5>xO$Q|IBf8;@sY+VUL7At;AL)KHY*6LyO|X|crmquh zCNZ-SQC394Xsm2U2xz3(mOO%NFhDozYsGkHB^-@UT5C+q30)rf^5LG04^Nf0wYZ_T z?3`a$N5;KxDG4pSEl*scZ}&Lf0))^ct(W6DrAUpXV5EjGI)r(PjSmR}KTh7#*&X!7 z+h~z)z+?r||K1r1`_m!xkPtk{PCD^L^PPboQ9VGRM9NxjMY%WVY;vl5GPE-|+!KuK z3MSS<wzJybZ}G08L`D#eNfLWNnhfOuzsT*P{0a9K)Nm&sJHJb}9CXlD&`Q*r{NY+7 zv(-yMl!JwHW&Mxt04-_7HY0-AJJSbF9FQa7Gmh~%AW?cmn>iy@+Yz5vM3L2`XLQyQ zhP%QeS?K~GY^Pgv(C(A9P#?yd@ZQ$FtGp<e>a9ADNe?Bx_qQ1tOw3u80LBPf?Bc{4 zxS@wAfw4)jsRh21?nLmn2IN<h-GPx7PVk?W0+xd*JA)!-=9hZ8%ZZ`nGl7`$us7bR z99O4~m<xm-J!x|VP0=jQ3$9o$=n#g05H`95f0iVDKrZzNFscU(KXwKc`+ZH4>YKX) z%}P<J(FlcdG(WLB$iRgs>5A)54~x$6m{1)eVXd}La*|;{Kzrcb0nkwMATwnrwAX5u z0~+j>>k*z%BC~dIg?Lwn$#jf;0auE@U+|XAjght?!>j^dVn}f@3<4F*YQB>@I6{CW z`obaN+xO?dT%{a<G(m^Btzd-6uAoXMFy3V${y!+|7W)513;8gKClD@B-%<{y<Qv2L z^v6(BGXE#FdpI)yc6XnbcoR0Xq4d6CxVncklF)hkVtmq&M0z4ix<JiZGOV8jsFDx} zYm$-5Ndzj&!vbQNyclcV_>q>#ok8vf)Y;rWtHd4`hPS<u$MqPVKc*lK;SprIh|Ho$ zMR(6bB#K*h2Q)C$kRc(3{0Kx;JA@MX=eI=OkN5;D6GbdC6Z`rP?umV3m&?c4UgeKE z7<u1Bm0V*(e!sw@Acs{`)}|P@*S8d)<Si^+3g9V=FcpgCfG`2s#yw4I$-hKDljZdw zN&?-!C(%fMce}-Xz`@&XLqo99<6~H$rjPi_?)F&klwy~7PzFrO0{4rI$wXULLEc!C z$HbLHJU;euWN+_Olc78z4BQuJHW|PoMkvIjxMF!{I7!7~g0I_JafYyw41xj5<mnmT z2TVI@X_Nu`W3mxOIS%3(h^~Yrf{R;CaO5VVcgY<IF?P#YhP7mZys1PZmWGX;WHk+5 zB;a7iC)L}kBNm%2q@(ic6rQXNQxX_7tI6^QuX9|~Atg0SLF6+{*)OpiEGY@pAGF98 zc@m0J{{s?oVraP1@Lws2a=`9j!;Tc0tNlMDiR~@G{txTYlY5gU%4DLEpy3(@)!}+S z?I$9ZHtGaV3&RH_t5CkLJa>ch|7V;9j7iFbFe%WWi5R(u$IbsY|D6%ZbtnZ`AXMyL zEAHtuq#7_L4fhM`K)Z8fpP(h)!}81~kiY}gpAeSVhInZgmj98pdh)T4J7`Hy-~^qr zN#NX7{@cuDODraL4g-;qSl{Q&k@?1+8cek^RSmFgxqXr7Z48_YA7l@AxuDnY2!-yf z@!E8i5PCP}Dt5t~&J}-D*IF<$BcLhZVNPP96poHJ;@WZQbThjy7zBHirdt!oqFx$| z&`A578KwwsYx<ff(+fhtj&<Ub8R+|?T;{+)wek^6lD&k=PZAK8bO!zJ6=sSKp>9lE ze{Q-X)9#7mA)A)}=SR?PAi9LDSu+y27#O)8yII%uosa6U#TxPP2Iu@`ci@%omO^0o zlER3MUmO=*dG@3^0e@vFpu0ELg(w$B-@b)_!6^ZEvSK-KLzF9(d0D`1cpR7yalS)$ z;Je4NKji?OKq!_w_6a8CAdN82EoaJFcu<PuMjpWcm5)qsArHN6v3M3q_GCB-l1>6m zIPy|CD(%ucr!MyEwT2=2R<Di~d%P7|LS`<Kq7rqqlXh%99!h_H2votldZUOTV?@GP zihM2hYs*RAR$}~#xs2q~l467iU1{Q9Qtrw8;DRTuHzo2z2t`SLY)P1CRVh-DuA8yl z%zJ-z2moV*V)^8CAuvgSVOP$wp+jgQQJnb>z7HM-?cXPSFqN;q{YkGRp!nzPU&Wiy za_R8IpON|jO(wG0qcC)+cmvm_8=zp)9TYDGaWswjhOGkzCtR^(<HIqZrQklrFNQl8 z43cF{j=|bVbhRx*LM4=Grci_3!N6sSC=;7FHX1OE-04p&{}*Je*d=C2>z3=n!tTI? zfkJ9)LH83R!6gB5RbJ$JRp_#Vix6Z9kr*{KO1xH9Oy_+F-`rv!`Gf;vA(4Wv!#Z>M zun{-=`@}X{g%>)613p{D8-JBVzz&MOpl%r4g2FG!g|Da8H_2hz8Q9the?zAjcBB$1 zYaV+T@$M8|6Ps+W6tO%kvLjgXi7wm~G%-5uPx`{jpw|enffN{MvB#%{mcXG7UE8ov zIpBq%U|1+IFg$s2>|$crGJ$syBdmD%Zn6HGfB_a<8o4HnTWA&aVV7=!rJ%)-@Vq(8 zJL!qx^P-v??UoJb3*2IHx1f|(-AZ-`g$`vh!f6IBm3wBGUYJt7LvV>vND-ksG2m_@ zeye;g2)Z+1M{N+o{fSV66v_i(=~azUnK+Z$diAkfi!F4GZHWpswMcPkpnze5xpY4# z_yD;-f92JHh!PqX>Ul?=x7^3&o~;uh&F5%J)&Wxs2|*Ai(M6YZ@lr3Cx9iFm`ydj@ zAUv%RQh8ta1-gPeG!oUa0XQJBhPr7U7#o8rM=%Rq9n`mkl=g%@+`J4P4iN|yo52w{ z6W^ccRJ9iV&n0_<^M0yDeuNvk@<2~Ux`zXL%3rro)-7VsF`-xaF`!s(*&&4Uc?mgf zhaBk%uxc=pmi*wkPV<d>wC2@Zo#-mEvt<Lkq#P8PZ<-t%WoYn)L(P^U6wBHrEirna z5u6p5g;(&RH#+yXdIyDuK>^{m9hIGjB697(h<su+<(qzWG{)Qo98n<dbS-j^;6{ca z#;2BoWBL%fyWxE^IOQlX{C9ZYfaHyJV8x>x%tYSAu`$h1{?9tr%dwfkrZa|tmK>CT z9-LeQu>ZfW(IF>>W)e7gl8#n)64E-%%8}a8_!`BNJ4r~xh~G9|uO@lul<eVQyyM$J z=h)ySZV)T*zfQtaq$Zi1IJx?0C_G)5FI8~C;{++)H-jel1xDxWkqIwzrEE1;h~Eo> zt`RpgiAs|5Vh(On4u-T8TWpT>u5SWEW}*Z_lXT+Z=q14fgfPKfZY%{w2#Wh-aa_Sg z$vwh+9Um_xfat5p<kh3c3xPCVKBkl~HhyB8v<h34%0*VH<L||qrEkS?E=-^+=nf{N z67FpFK>?|B!Llo9NpXEFKa~_2Q;blO9v&To7@5rigz$KKVA(w~wdC)lOR*zbsUfk@ zu;83-NAc|ME>rmeDDwBk8)T)j447ON4*ve$cPyTrd!{?&e5`ctMiN_pCm2pL9J<sG z3YfT5==1NLZ(MQ`8JN&rr?fMKvf^vPv`RXgFAQ-=jcsA}_dT1@hJ*o<W6PSEol=`z z4{ELhI<G1rx(4Pbzz3wHAzo-&icB}UNgzxdO}|!xBd354qAU2m*iAYFV^^?bfuO@d z5pPrbEFHFn_^fCV@}hq}=ylNYf}l204v75;d7`NN!^AM;@_3{iv{#34bxPi9^OGvX zv+ypBj|^#ud}5->iGNsS$4GbcknX6+r2%@S3vgED<!-WH9I|7?;8cmO5HG^wfWX6` zjpU$C>QQzG;&Qqea-F<J2Gj-H`C&3kaZdn3Cg$QHQY42K`8udDwj5geh5OC@2}9Va zx>C#?f(N;FxlnhYS{e@mGdt^05?Mqsd+0`2#q!DA$c~cGB+RuhDGxouqDA>X^O8S~ z#1<@ntzu*0xK|9^&3Rm&)w^(w99<;6#L_!7!@syqGzGjWG%7GP(tXQS1=oeK)8M{j zphQ-}6B;DjIg;>maX?~u*^<`#1z%h43bJKL$e(uxQ`E{;O2dvT!;|*sCceNfy8~#n zKvax0$H;B^{Z6htvHAib9M9|!ng9??Ad2lZG(40s*M1}(2lk`Vg<!Pb`f46T-08eN zZ9{u;mHm%fM65kODrRGvi6l_`9<sX$-B(2Es}86zr$18xGg}kMVj7M>!2zX4Lo#bu zP5nt^#Brw-hNp^F0XY!U5*v0GF4&|Szz}1GZ{hLffnS!~dZTYdD7=TdSI1|ixS>iN z;I>z^mxB`ECAPlyeiW$2>{JJZs=wu8fl%V1U4f_(qf9UzsUygz0>!_DcFY&~Mj#7E z7~0FP5{8VvIaB&f%-kpmfFlw%p7at;kc4HA4+=>n5*>d8YXZL;(HT4$xQKa&>2qe% zJ}h~;EA`iyU;I_pCFC#F%vD4Qxj&)JQR3Z)aF+F|PvvrhRB?&b(eTQM6GIinvJ4E- zA$;`*E&lrCBEbPm?zUq<C-Z9gN79%2h=JlshvrWI<3DGJmh02_$+~a|&hkuy&l*1C z1iIy<^r+(uEJMN*Zsxh<(#S+L&_^cr2Yg+u_EINinn|b^(C`}6xEgRo{j4$Gywhfa zw|@5zj!)k^WHmmK!R>pSzVVSFE%HxzTEI^z4Lg&SWauuzBymVQa!e$zAa({NWxfoi z?D7l&N5~)~Z!K6GNNOdQyp1=u{-CQ0SW)~=fJ<wjh_D|S_?p};*1T^#H&7j+wp45_ zLXF&0Ls8{l=B^~<(VEO&$SY7xa?d~~Lptzb+*JACl6g@erh}J(*cnKa`#*bxE+EB- zY(rgmm4YH?hAu*cE=GnPF>X&P+YL*EiE_a3=Ogj&_D`-cx`OfEDVAJm-1ja;`fSHH z6OvloPf^*9OczHWtK-E@%0KSNs@Dc{-xwT^g48{n(#;AO77A>RW?+GU&Gi%ZM9}j* zF)YUwVL){^2timPHEMzPe-gL8Vj&q+#fb-ybl*SW#~8kgKX_NZ?HO1qMAGhKN&o}< z5vAD*#o3dv3*=V!Y`4P}%<A@9qwP=%J;6zR(GciI7@1ewYdQdoPkt9}d0ZOB`Cyiz z$b3iJ$T#BvOZC3cJN1kd@|?6D@!m6ACNR($K_!(#59AigLL8CRn!Hw$^=>*SG$ymR zOc<Xj_jaNa;E`wjhc98O2jK|$tVW&4QeYy$eV-t3%`YF-vRvWfdD8eloUbCuZ_&um zL;!a_s%5n}w&)Pj;@B9@@+}9D$CniVQNOyhj|qtP;$s<2N62a%P)iyaz9k`~j*X-^ zo*#IL#gR2wsS{TPn7Jng&E0fuSd->U(Xqi2J9=hm#8GiX7&3@GOS*z7l0+QQ7zRfF zK`m(&o(GNJC>gqGz^qsL^uh46F6CfliaWZ4Co$SEEkrrU1Y9?A!?8I^7A8D0PaF;- zzwAGf>pPJqJN<$ao599iLAAW`)ty=Jaae5mwTV>#E(sUEz%1)(nft)<*d!l)LGWWU z_XhATJVA7d$KF!<&A9T=85Fn@GqVQ{)-JLl$kE7S9{BbnX^rOP_hEpB+}Mg2Y5cVW zoR=7Zoddgs=ha4p$Ip^69D6u+*h|=|k;uT#@j!?GU(?NLn!-dG66|U(?ZHC^d1{ho z@RBZT`IJm1kDR8Shk8HZErAfXPZGuaTv14tSCGoUw!EPyQA$Lc5F$`dZ4e_Y85AIG z4XMyL8Np1WvSBXn=nkg(6$s0V-D4AE))ByJ&3Ev<!d2-GFfa^YlkcM)*%%05Y>+{a z@pplu7F$6LWqgvWEZ4?JYFu!H<83!<9!ug48Ib&*sI<5wFekm`k+JV&3h~%D4846M zE}YaD5Ff^j^Z~{4UeT*LI6kiVGb1nwDq|8mzm-J69-J-WKhi-(qZflagSsWEdZz4% zG6{|{*%cu7VuP%*@p|B`m=}d$8tlI}CAssMNGlTeYZB2M(dAo)1!bhXj0|{I;yDqp zYOLbfaUanj7^2KyMh5JoUs+8|bL^jT@QQ5fA0xvSKruA-1CWS;U{ijEZj$l<Tr^&% zOWJSJv1{PvJdHpCT&@^<KzZ5FRzy#RGPv<O1klY7j98;qLw~a5<UM-CEXU4(PchLP z)EAhAY!Rhk$`t{NC+-a<KJt9c5_S;X!SYZrS1MCRbi5=?#-w~&(u1k2kB}mv@#7v* zcZv{WH^5jo6#tah%pUXe6EHJcjzX8yb5ak@!AP_T_Xby|G~~({Ke@eeYZbM_Ff<un zCAQg7JLt~W8!K}Yk$y`?StZiHBc(xTuTRhtH64naM=c4xCC`BE2jm0KJ6Wx#UXAL8 zfmg#O)1U;olP|d^KcSW?y66hJ1O&`a&_a!C!}Ersh(s|I%R}}EOIGqrU!xQv=(_|% z!>=Xfh2a(MdR`^rNj*~i=h7HpVtBFriKjgng7)j*VW<YP?#4;_)oj9@vK0O-Bq@~! zdt(!#d|XqwOz6Hg+Xzv(Yec7}dj>1aVxc1#CpY1dt%BdlU}ckA;hIqno-{hOoFWMO zW61nK@)%`$1oQwO1)?BMPos;Ld?h{n5Yd4)<TR861N2~4$=f+e#S1|bUJ-@A0C|we zUvc6wwMR>`ha`PqV0#gY!zEAsmQpaf_?Hx-GG$S@Q=CxoZe9g>=!A<Rq{p=vbP;iH z&){{<hG^kgcoOqeWH-Xdm?8gLvYcnl*jX<HxXLmaiXxRE2;6xBdz4*4iDBW%7Qy7& zIAv!rvOB2dBTi^JATXymp(!T8rCp-i)E=BG#gC^6NgdbMgtEX7-&HM>soI6O-d*|s z4SLQK!$>f3b?c2kTl!=#g6~EGipS0eky9w4Rh-rb?#uV&w_KAGme+<3A=YKMrbKG{ z!3r_c+3!X2R{04rI^Tx+-ieSf4%kPqxwIp(Vp$Umk_A^e{=Zp732&*?b?Xhz3>`bP zfwBL7HxQ*X{FM>ce8b+l`q2mho%LVwftG^-$@5{z(UPh+2+7~l&libnTiEI~Q5|A6 z6qJAhXCkB7LZ(<!-$+DKXW}R8&5Bn3p)ZuE7H7SH3RlE`zKL;>Cmn~8CtR9p)P7|$ zjV#Rd2tbi}n^gydp5dDt`;mJqCi;k-!HVQHau0*<`vxCQxgv;$Al8dht_XuVrS{H! ze*sqcwd(KFovi8egxA5FW?JxA;)P?lYehMjkR_d465wev0juu+&?7C`AK-{I)f&7? zdW8*>7MBSFKR8^+>!S<%zR?DoBb*--yB|gDr1UEE2Jm4QzcSQ|^gMF&<HDQT`-y>J zjBIf5nh2MoUl6KrmPe*EvK%bn)aJ~j;I>|oe=q^@HHmBN0Jv)5hqc_#4p73ZV|Q@S z_`R~2q$Dt;GYDxdmf=neV3<PZ#;?hFVdj$>4n<4p0M&-!>g}`x^n7W@7g)>;d>_X@ zo1uJon9|~7J7e#Cad)-VTp5~crX5k(gzicSbU>Npi_A{pmVhd;)fF>glCK`$2J^Z& zvm6*TTP+Fdn+67Z=?tFyoAEI!C`H{;dtWt$?WkmNd~`}h&Xzr|k8y2S-w@=LC%af8 zOo?#Y@5GMCYHA@#LS$PJ&a!=C+dkz08E3a^nw?+RCSJdp-%f+zYv$DHuax@X{uhLn zHg=4dyness4TJWLg+vhYE(u4xoxui58W0_)?H`$WOG?mgb3@<COt}%t!4m|<2yJZw zJ2-+*E*XOu*+q>8f3URp=bjZ&c3UEHCOQN_&?kPUX2|opi8f7qOed2WC*{s9G3~vE z0>W)f6;bA~w?FcZ<r7^b%ybFPq=(A+2}(T0#$EYiUFhmdGQ3cCab$!9x@FUEezJ=# zDDXFa-`OaM*#00}<q40q2DMfQkE0u~92Oen(gTCS6=TOIcIWqg;}gu%iTqh#U}`yN z9P?Z1ZDOZ5<VTksLYE6eW{*Dab|QBMeqtakLDCsWPY6_!(a13VO-}CpBSH{4s}2mi z(9<+OB*iTqf({E)A`g9}bycd#dN4RaBJJhiJu$+LlbUnvo1RSCgT8*kvAb9HEe7e) z?7$+;#GKqFhcTeD=efJ{wXm`?=*$S*Nyc7VO2I^ufYAj{gf^quc%c*(Z<vJ^pa*)^ zWE*t=hnOeFwgn4PMGG=$A2>L<bXwF*j0z*IYKG2XxbA>)vR5DLF6SZoo1L#yel21Z zf6~?{=>%C%LW>02S#1BRKvUWWi&St-Y;X#(ZblxwCFcWRO<(BY?n9$cvJJPOOG>6U zMoN%C-#8x)oF=Y=HLxWt128G9ok@+nof<lZV*oc5>ZsL;cs{E^`wR{vSBi0Qs3I{6 zE@=PMlK;XaO-W=JR4<dcEZsru7Db2<8U%Q1F*H1HP7;?0?UVDoWshJ<Ppe?oB|#7n z8G=LDdP5hS12~?w$cV2f1^l#fTr<e<4pxK*evA&|HeHNm0(xNnh4lfU<&4}()aOc> zH&H5yu!6smMa^YmNnjeoTJpK+i9g~V1tH>o&F2XsmJVQ)7wF6H?x3POz~47~4QEA- zJt+TiTIiCqV$$%fOlE6v)+|FPnBh7QG43@xg07c2dxx8%GkAh)!Wzm@S0*(w;UwDj zn$}?ej{R#g@DsZ}(deEgOh64f`)c7MbvJOp&zGDvcg6~bHH#6M5-b<aiHT1v@W)Zz z_ey<1<qczHVtXJ_hYkqkqa;+uNq@&Pmo68EE!gCjq*<qk@%L^$hKLq=l!GT*gz?eu zop(97LpBbML%Yl0vlG~LPyu3K(440Kay_RU=~5Q&i)a!kbaE%l1<f)M_LCy8qDx4Q zpC*l8eh}iOHade%SsqN=w{ZDi{g2b|E^c!2?qWxxq-H>j9HpRX{D({vvx{^Q$JNus z;XHC{3d`T~-d4@GS90T9-SL87ID{-52-*R)m9d2znyt<N=x>z!&T?@783hc#p6#~U zIHr#FdDN>M0j6M-nPtpPPLuNk^#ynG;xyJ1OE_xEfBy-43NJAy4fHI-!dD$4uu~#F z#*ZU~I_jR;5Tv)pt7M7z%Y^OTVn}#AV-?dBN$DHqp)0+X7Do;VEl%Q(qDtQCWc&Pe zy~{&f6gFw$D;51^d{oO_#0UycEb$9J;p+?LvU13}3=1o-46hhBqmkiDLzu!@en+=? zhtNSDJj4<%Kl9g_r@N(B+bVfxgcRdl1)#_iQh-powDhA)I5&TH(fjBF;c|-@jhWZp z=)Nw`8#=Pd!wY+~e0g(!KtTRFu+<y>7z33mez2OYRIDykx4E;6@YG<)XBbs(BV*e( z-YxfVh}@Ov4w|n3?gO~d!<!|)9+$=#5MmY7GOKsOvgM=7L=GaBlD&x+LAEv#%-AH5 zWFpEuu8ZqKPh2@9umuZ_fkYYH`wrKl{4aNmY+NJ55az~9ZgME$geuj4f%;KP&CH@@ zmk>LIoe^$hKnXKzn9}{zX-U0mE161tq;fepD22Hy3ll^A+yTEUMAaIM1Rqq=Y5pk( z=ac6=Kg0wfF8Y*VLBY!(mb@{IKM9-T$SC;hh>sQ%aPL5;)z{VWZE@6tW6?wghjV32 zf$R`?uAzI#j_CY4zQq$(fhkw)jt$^M^K;7qu?*OBtbydyfr7NuStp$Vd6@i#XOYpy zn6^edCv0o6WJr>}cLp_;h>LSiBP|@SO1|X=e$iVB7Tzyu()Rn{!ezmb>D=)mnz*i! z{n6=i9->AhadD-V=?c_2p&vT&wtx|jFb!LF-o1(}X)%$(KD7hV&53B{fuGQOrd!6a zu&^2KCx<2y63j#^j742{X(zjB|Njm?C#z>8Q&m0HWOp!g_Y_*l0-Ot7h;*Fs+ejdM zB+U(BV$#hR5lda7n5!)R?eBdUm>)1=%90*^yC>hIekO?}wZ!t@#}M(n3Qoushj7W) zi>Mf$CRr&yd3iM9C6T7MfJxHm_x+)!4V5e!7Wzf}fh#kNL$)Vtc;KW`y*!Wb$0(>B z0k^&tZXJo2g&}hS59MhB6w(gagzuCW5#2H1O^C?Qdl11zlHI_Vsg{a;0YvP(W$*%L zpWA{CDC&k%ct#c;c;9^ToY);$5^8M0q}<Vy;wJ9HLI*JMeB8}Pj1LQq-|$3FpLv1* zHT##D+Iyxg*PZRVdzvgKs{js_gbq>CIlB-O&;ZWHjbF)AzX?9A^NNp^BHscNr2!Gn zUZ^L2yGCVH&HW>d%XMT&5MgpX)`w5X4^4jCy{5II?!+*>UT8f!ER+I@4vt(J>j!7j z-#!I#Fh2SC7cuge)Lp38iTlPV5wuI|E-{iBis+KGL!eY~NJ%&}GK@Er_x7tr!d)Bo z2<PQ%Vi3dgJ~OPaIQqqL<jHO=`GGSIV#{18xL|q;GU@sFjQxf0fs7UrOAbjNj#l%Q zlF%**1?#ZcQ(ec~nAL0)y%eA$WZF2V3c@f9mRI7n7sLq*;)FTyVe2h#7lV7X`4^LF zDByh|b-z;+xvlp$YIw9eVBXY9M>JcLVL_>A%++9e;}srQzTq}1UGPpQ5$N()Y&)EI zT!nU4@*(6zF<>p=%82#?!&|_DoKRCbw9uoG>)ruI?3RUSVsU4_1#{&1#QBmswK!jP zF)j0Y&_$;-F}~5E8bWJJtp)OE-ImldLt`sG>)^gq>R$#huFq(%Ijl`y@OdD3GCySh zy_44nKpbz0QLGsEm!~u&bzsQFk*Ot1yn!aUO^q0KOPS)tnGO-4-XWAv8Ux8`sXvfX zP$S2U0WCH7QakZskwkFf`1nK*Mk<k=Gz>1h5or`!a77!9myD=}Bi{du+-j8+$_)$! zgL?y7PdL6T?AZ*TK-#c)IGduM-e7ZYLR3~1vd0S!mz6y=1|*dBBV7}=6+9M<h03-7 z!v8(m-CfObir71{m|kHEWTwIg_2g+`A+4V9KUhg3SEXcnQkNjtJ6!i6q3KX}6Q#_B z%stV74nR{>hiB#76Q0QyXJmro;?@tn$O5~K`YmcL?!CBpn1o*!sg3UP=oc|9ED<7~ z*fBhrCo^&wU^Lt4@(v;ZZ@t(j-W9OL3V^EpN2bb<p`m?D%kAI1(YI;Y1cW$0fWQ(@ z`tv<0L{|`}%a%c}FBpQw;SolLfvY2ZO)K})p@z}C=lwc5KE6+!|B=;%JKw@xo$Iqz z-a~`?MyrIRmt>Q#k-26Ya9UE14_J@@13VE}G-n#51|P;HrGOm*1_X8mm-o0<9yn;H z2<Oa1?I0I(NO0H@al#x~KZb;ZQ>jdT`QSxm%M0Imc8@>K8_hlp%4KE$v7u#a=YvE# zAwPO@TG&u=BOIVZ@enWfF5U#ZC7~R8Qag~^3D*XTXtYs{i0{~Fc`&$N6XqK1k#VW~ zi5p+9CMdPDa(&c0iN_l|R~(D%q;w?Kg(7z1DkMt6ghQkXLVA>Bo$!tvu_?ST7_J9} zRJ0Ld1OMOkH87;LK-EK2R*5aK$@x;}g<yXA{4NBrDg#B~1);uwk=HbF1c>k@s2L~$ zeSBn!Gtz*w)l^*4mcyev03w{_y;D?8lIxfJeLbZlv`8>?S$O=gyzPWQq2@)OBA?}9 z>a2dAQW1rx3B(M*s&Fl$IUJA$#BRUB%x*X>FjAOLaff(v%~Zj}N1?>*^Mw34N&pqQ zwDQ%o^>zGC<Lkoiu|qo$G9n1DffQ%-pHnH<TcivQ^OJ1?y91=(6#SQGm)#mx@{Bmg ztMv(C2F(hLHe4o3%OOZ*@GKZl*0>qo^2HkR(w;cQoRXL*c2j#r4|k1G?%|;%g#O3* z$wjCs36D)6`6tyfOd;+({EP_p^fef)ED1Z@RVqbP#CnZfgvbjFc|eUJ;z@(DvQE|q zk*p9vdvigZ9W>I$80|&-5EPDJ$VYYtAgH-|IS}k%E%zh3r1cpVI&NO<5R68K8mER7 z9_S9iwMi~4ZFm4JzMD^`nr5%=KT2LD4`WeI4%y-?ai}%7c=Vs}TWf%yPgroqkRjnj zVv_eKWS~%^Hbi?r&^2rs_Zhme8RWWX&010l1Qiw=j4F|#Y~3r|5C<fudmT7=X=J=R zqhU7M&c<}d9>c;D`OOj8up-voy93}T0&Oz#RoW}rK{GG&aX`dAmKuz>7T}M_Z*K@n zTS>nkxVpZ;Wa#8Vj11&w6{4qEBm|kFgeb)LKyA2(y0{ZL)=;trMdA~a#U0<!drSdW zg(0<wUCA=F-FF4$6RTvB#dKsVTwaU<G7jNsYVrb~?@NMe3|^L``pqxa;FT>c!{-da zU1TgHcKkq^$qHmCX$ZbboHB*Z{4Cv)Fry@-dihlj+JD#_u|F7iXG0S1oZ;1ljnwm8 zw^x1_v<^oW!uVr=GU|y$p)fBozP(N4z&)Y)6DH+b3TQ^2CWgx*q6B>WSC>PxyUqM_ z(q-X5Z6m}wY2CwGvB4R@r6vaoSHgJDala!r#cPhytx@dMkxhhE5W^O3DTrG!r;RBY zZ*XMxBF3@#ks%p92w2udOaT(s$SHrB384<*1>rt`6))(ClU(1?J*V=&f|`y6=kJgB zhXQp!CkzR1SWwb9xUr+=;V6a#RZ?+R5Izj?qwsYh8?5->j9S`9kBM&yVrr14OcGO8 zhbkZ}iY}=#94PVZyba6oV(0#VyMkyLnp_RWr*0^j6hi{(f)Sq@tTEjwZm~MfWTRM> z9fI2(fngtFfo`}wo?tDzHfhA*@T()C<H=y7F8hL+B+970{Gv<PM)YhI58%qXMC=}y zx+8ja4dWy<9~?wL4@T0P1{i%jDh_>7X(~6FLeZZP0oCQ%(bQ9oJGb;@AS>L{OolT- zo!8JL8xM=z0dhE^|6In{(AP~cFkfc{ljQ@9y38>Shhj;Io|l4=p+*Frx0Bfj9ip%x zB}l7&3{6guNn!as=!SMf>I=H3O7YK(fHXRdoRed|hxbN?Dat(&NW2Bc<zR?7s$gwc zxhCBC{Wp^shm+ov5GZGA`;$Q{_*8C&Ev9!#I|J$ucz6W#pnrlA6uZ9VD6l`JphmC% zq@<|J$-zF7BXUrxWERcP9z01y;1XedT+c~U4BoO8`Ai*R*zvM@rMxytbF3m)91({| zAK?>cI^$Qqr8GPeZur32rxf_k;K>xVnk9id`dvj}iVtHj5HS-h+SUx~L2B&pU-3So z%3;FgddMW3F$O<LL8BM4<m;1S+h7r|BzN{aZ#|I^HUQK4eFydt4=LbOv|el`7aDR~ z8`;Ynja`+JfPX%)d{`%6_)Qvsz??Q^5};ynO~{oh!xVnCm+r*w03O#pk~jh`Y=E05 z)Gq~&k&1uU(D-4vg3}p&{YL$$D=BFQ-}>1@(cpEYhnw+9dpwCe<W-zJdv9GCcG1o* z>I#}fw^L`b<MH=T>3Nj|esWnFp11v|e$Ef-X|i{=b8YHDP$=SjlLNz_FyB!IugLex zV51`F^%ger=nfdrGE15mq8#x2%`?M>nJ~-@n#gcIc})X|<@*M-<RkGy5MIC`fww!t zYhophgo`I91}^kRVg_Q?v+x^)mD;MX4HwpeR2&Wy+6vAPK}6%|vITK-caN4~E3-<- z@yQIp2qHnpFlFKG*Zh?S_JKu|0r46dP%Lnlfdf*2hjJ^5gYN>XZW+(U{lSC^wfc+d z{%)xk#`y9iCYg#LLJp2l)I}ZCP=_fc7}H-~zLgLf+OEh^#HnVxY{aJu90dkC??I<C z{|l2K#GRo}10R8Ki_C=5{<-An2|_E~4{<fv^5KK-G4=&L@5cUNJCeDIANxqBuq1hH zNlBQI-2S}N7V1ZYU{WTOn!YktDA$Hn9{Vn3q03HSN*4Nfm5(QL-|N4z1+O$mMz@XE zB<hTA?x(|pzf+fV%R&7=<semxR}>e^{*cm0f#yo_)qZsu9PR_SqeTc^<8FC=I9TZJ zEx5ui4uM4y3t@;SN$Ttbm<$xzDwg(gSW})L=NXQu;l`V9C?Mm=;<U-EvrV|@eoyH) zA!-WD8WO9`TuD^AQ{a7TdU4&UGmncSzMTVS@`dY4h_)MFqcYz_NCJ(@xG+$lJ&BM) z*=*!dpC%9kbU}t^szNq0m<xA=HW+b-G@GvM4y>f*vDJhYV|VM{6^c3<kBM8hG+oNU zk<)GwpRMk+jUTop+bb;tCMBQ;E7kwLe(jA3Gvod-2L=-twAvhxMl5w15b%>?0O)U< zQtSt2ijvdmeBM{aHUmHR4rak+yE6%>Fu8+~!t4%)=#qvA5&BELoaF%Q<)60Edv+e% zd%8w+d^^fbEm4D?_)152Agud6U-6h;E@mbpS;VIzNWn_xt{VBNJ3s2Yf9-{}VJ78x z_qZWxp68u3(1aY=m7buMoI*pc<syMy!oui(+pm*ZlN*HFK4XjFN9PVq@;?gY`sofN zsXls_ph?Vpb{ZZ&R<i+yIn|jHySP9iEtg&0-eWb{o!}Q)NGhy!h5JXq!#GM99v&Zr zR;q&<&b<bASyvYtZp34NG~aLsWY{8q%aiG9Q;?QT7FS*Twj2;5daYz{`g%Ea84Q~2 zlxF<%C&dR<_KG#MN~vg8e!&)KVrMB(@4-koU|b@Hn3#(U4I2YOt63jf7i8N0OH2}1 ziAeq&_*_A-{G?2AOCrO3^OpBWVPFgm$V^YkqMM#P&aR5Hw~AFw4~-|Lv$)dcO{aeL zj$3xK*hB7~Y0F0J&*XK9A8APmwk{<>e8;CqnwIf*0$1}y!_$=r&{C>L-TjnUrmNJY z)HfVeH7ykj_=%{qMTk!g%!1C83X-l)s>rNm%p)%h(^o=qh3Q);Mq~rA1E_ciD^$EB zI>jLb&sbk*a?oZu^9FjQ*z#d6JR|V}G^;LegkffPh4%d-k;Fp(!Kg{H?V@y8mYWPW zGN93KQ^33>QGC{ASm^JPfFbkAlfstKb$v=gO?s<z2C*y1dSRkLsv=0>j&XhHY3W#e z5|KRKIQG~xj?Z7WM3WuObS{!!($no6`vM@zSq@@gC=kU3T(za8=3X4Dmx3q<^)ob# zR4?ZRvip9q((Kc-XNv^GmeQkjLcsBG2kZ58zNQBs&+HZN?%z#r2@h@=bG$zv;|S-F s3x*=Qri}^xL>ef5Ck*N2L2;(SF0#G4gpkbiYmgN8KUId9=vfa00K}cLD*ylh -- 1.5.6.5
14Â years, 2Â months
1
0
0
0
last-svn-commit-30-gacb2f3d Turn around Millet 2008 hsv descriptors.
by Yann Jacquelet
* 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
14Â years, 2Â months
1
0
0
0
last-svn-commit-48-g6ada0c4 Import annotating sources from milena green's sandbox.
by Yann Jacquelet
* README.green: New. * demo/annotating/bic/Makefile.am: New. * demo/annotating/bic/bic.cc: New. * demo/annotating/hsv/Makefile.am: New. * demo/annotating/hsv/hsv.cc: New. * demo/annotating/lep/Makefile.am: New. * demo/annotating/lep/lep.cc: New. * demo/annotating/nb_color/Makefile.am: New. * demo/annotating/nb_color/nb_color.cc: New. * demo/annotating/project/Makefile.am: New. * demo/annotating/project/project.cc: New. * demo/annotating/rgb_64/Makefile.am: New. * demo/annotating/rgb_64/rgb_64.cc: New. * demo/annotating/rgb_64_9/Makefile.am: New. * demo/annotating/rgb_64_9/rgb_64_9.cc: New. * demo/annotating/stddev_color/Makefile.am: New. * demo/annotating/stddev_color/stddev_color.cc: New. * demo/annotating/stddev_color_16/Makefile.am: New. * demo/annotating/stddev_color_16/stddev_color_16.cc: New. * exp/annotating/achromastism/Makefile.am: New. * exp/annotating/achromastism/achromastism.cc: New. * exp/annotating/achromastism/text-color.txt: New. * exp/annotating/achromastism/text-img.txt: New. * exp/annotating/achromastism/text-only.txt: New. * exp/annotating/bench/Makefile.am: New. * exp/annotating/bench/bench.cc: New. * exp/annotating/error/Makefile.am: New. * exp/annotating/error/error.cc: New. * exp/annotating/histo/Makefile.am: New. * exp/annotating/histo/histo.cc: New. * exp/annotating/hsv/Makefile.am: New. * exp/annotating/hsv/hsv.cc: New. * exp/annotating/hue/Makefile.am: New. * exp/annotating/hue/hue.cc: New. * exp/annotating/hue/text-color.txt: New. * exp/annotating/hue/text-img.txt: New. * exp/annotating/hue/text-only.txt: New. * exp/annotating/nb_color/Makefile.am: New. * exp/annotating/nb_color/nb_color.cc: New. * exp/annotating/saturation/Makefile.am: New. * exp/annotating/saturation/saturation.cc: New. * exp/annotating/saturation/text-color.txt: New. * exp/annotating/saturation/text-img.txt: New. * exp/annotating/saturation/text-only.txt: New. * exp/annotating/stddev_color/Makefile.am: New. * exp/annotating/stddev_color/stddev_color.cc: New. * exp/annotating/stddev_color_16/Makefile.am: New. * exp/annotating/stddev_color_16/stddev_color_16.cc: New. * exp/annotating/value/Makefile.am: New. * exp/annotating/value/text-color.txt: New. * exp/annotating/value/text-img.txt: New. * exp/annotating/value/text-only.txt: New. * exp/annotating/value/value.cc: New. * mln/clustering/kmean2d.hh: New. * mln/fun/p2b/achromatic.hh: New. * mln/fun/v2v/hue_concentration.hh: New. * mln/fun/v2v/rgb_to_achromatism_map.hh: New. * mln/fun/v2v/rgb_to_hsv.hh: New. --- scribo/sandbox/green/ChangeLog | 64 + scribo/sandbox/green/README.green | 507 +++++++- .../kmean1d => annotating/bic}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/bic/bic.cc | 122 ++ .../kmean1d => annotating/hsv}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/hsv/hsv.cc | 721 ++++++++++ .../kmean1d => annotating/lep}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/lep/lep.cc | 127 ++ .../kmean1d => annotating/nb_color}/Makefile.am | 0 .../green/demo/annotating/nb_color/nb_color.cc | 143 ++ .../kmean1d => annotating/project}/Makefile.am | 0 .../green/demo/annotating/project/project.cc | 275 ++++ .../kmean1d => annotating/rgb_64}/Makefile.am | 0 .../sandbox/green/demo/annotating/rgb_64/rgb_64.cc | 80 ++ .../kmean1d => annotating/rgb_64_9}/Makefile.am | 0 .../green/demo/annotating/rgb_64_9/rgb_64_9.cc | 132 ++ .../stddev_color}/Makefile.am | 0 .../demo/annotating/stddev_color/stddev_color.cc | 191 +++ .../stddev_color_16}/Makefile.am | 0 .../annotating/stddev_color_16/stddev_color_16.cc | 261 ++++ .../achromastism}/Makefile.am | 0 .../exp/annotating/achromastism/achromastism.cc | 179 +++ .../exp/annotating/achromastism/text-color.txt | 0 .../green/exp/annotating/achromastism/text-img.txt | 0 .../exp/annotating/achromastism/text-only.txt | 0 .../bench}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/bench/bench.cc | 1450 ++++++++++++++++++++ .../error}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/error/error.cc | 833 +++++++++++ .../histo}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/histo/histo.cc | 366 +++++ .../hsv}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/hsv/hsv.cc | 912 ++++++++++++ .../hue}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/hue/hue.cc | 402 ++++++ .../green/exp/annotating/hue}/text-color.txt | 0 .../sandbox/green/exp/annotating/hue}/text-img.txt | 0 .../green/exp/annotating/hue}/text-only.txt | 0 .../nb_color}/Makefile.am | 0 .../green/exp/annotating/nb_color/nb_color.cc | 171 +++ .../saturation}/Makefile.am | 0 .../green/exp/annotating/saturation/saturation.cc | 175 +++ .../exp/annotating/saturation}/text-color.txt | 0 .../green/exp/annotating/saturation}/text-img.txt | 0 .../green/exp/annotating/saturation}/text-only.txt | 0 .../stddev_color}/Makefile.am | 0 .../exp/annotating/stddev_color/stddev_color.cc | 216 +++ .../stddev_color_16}/Makefile.am | 0 .../annotating/stddev_color_16/stddev_color_16.cc | 277 ++++ .../value}/Makefile.am | 0 .../green/exp/annotating/value}/text-color.txt | 0 .../green/exp/annotating/value}/text-img.txt | 0 .../green/exp/annotating/value}/text-only.txt | 0 scribo/sandbox/green/exp/annotating/value/value.cc | 468 +++++++ scribo/sandbox/green/mln/clustering/kmean2d.hh | 4 +- scribo/sandbox/green/mln/fun/p2b/achromatic.hh | 20 +- .../sandbox/green/mln/fun/v2v/hue_concentration.hh | 10 + .../green/mln/fun/v2v/rgb_to_achromatism_map.hh | 5 + scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh | 15 +- 59 files changed, 8105 insertions(+), 21 deletions(-) copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/bic}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/bic/bic.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/hsv}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/hsv/hsv.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/lep}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/lep/lep.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/nb_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/project}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/project/project.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/rgb_64}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/rgb_64_9}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/stddev_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/stddev_color_16}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/achromastism}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-color.txt (100%) copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-img.txt (100%) copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/bench}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/bench/bench.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/error}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/error/error.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/histo}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/histo/histo.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/hsv}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/hsv/hsv.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/hue}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/hue/hue.cc copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/nb_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/saturation}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/saturation/saturation.cc copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/stddev_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/stddev_color_16}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/value}/Makefile.am (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-only.txt (100%) create mode 100644 scribo/sandbox/green/exp/annotating/value/value.cc diff --git a/scribo/sandbox/green/ChangeLog b/scribo/sandbox/green/ChangeLog index 5e4cb27..6210a79 100644 --- a/scribo/sandbox/green/ChangeLog +++ b/scribo/sandbox/green/ChangeLog @@ -1,3 +1,67 @@ +2010-09-08 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Import annotating sources from milena green's sandbox. + + * README.green: New. + * demo/annotating/bic/Makefile.am: New. + * demo/annotating/bic/bic.cc: New. + * demo/annotating/hsv/Makefile.am: New. + * demo/annotating/hsv/hsv.cc: New. + * demo/annotating/lep/Makefile.am: New. + * demo/annotating/lep/lep.cc: New. + * demo/annotating/nb_color/Makefile.am: New. + * demo/annotating/nb_color/nb_color.cc: New. + * demo/annotating/project/Makefile.am: New. + * demo/annotating/project/project.cc: New. + * demo/annotating/rgb_64/Makefile.am: New. + * demo/annotating/rgb_64/rgb_64.cc: New. + * demo/annotating/rgb_64_9/Makefile.am: New. + * demo/annotating/rgb_64_9/rgb_64_9.cc: New. + * demo/annotating/stddev_color/Makefile.am: New. + * demo/annotating/stddev_color/stddev_color.cc: New. + * demo/annotating/stddev_color_16/Makefile.am: New. + * demo/annotating/stddev_color_16/stddev_color_16.cc: New. + * exp/annotating/achromastism/Makefile.am: New. + * exp/annotating/achromastism/achromastism.cc: New. + * exp/annotating/achromastism/text-color.txt: New. + * exp/annotating/achromastism/text-img.txt: New. + * exp/annotating/achromastism/text-only.txt: New. + * exp/annotating/bench/Makefile.am: New. + * exp/annotating/bench/bench.cc: New. + * exp/annotating/error/Makefile.am: New. + * exp/annotating/error/error.cc: New. + * exp/annotating/histo/Makefile.am: New. + * exp/annotating/histo/histo.cc: New. + * exp/annotating/hsv/Makefile.am: New. + * exp/annotating/hsv/hsv.cc: New. + * exp/annotating/hue/Makefile.am: New. + * exp/annotating/hue/hue.cc: New. + * exp/annotating/hue/text-color.txt: New. + * exp/annotating/hue/text-img.txt: New. + * exp/annotating/hue/text-only.txt: New. + * exp/annotating/nb_color/Makefile.am: New. + * exp/annotating/nb_color/nb_color.cc: New. + * exp/annotating/saturation/Makefile.am: New. + * exp/annotating/saturation/saturation.cc: New. + * exp/annotating/saturation/text-color.txt: New. + * exp/annotating/saturation/text-img.txt: New. + * exp/annotating/saturation/text-only.txt: New. + * exp/annotating/stddev_color/Makefile.am: New. + * exp/annotating/stddev_color/stddev_color.cc: New. + * exp/annotating/stddev_color_16/Makefile.am: New. + * exp/annotating/stddev_color_16/stddev_color_16.cc: New. + * exp/annotating/value/Makefile.am: New. + * exp/annotating/value/text-color.txt: New. + * exp/annotating/value/text-img.txt: New. + * exp/annotating/value/text-only.txt: New. + * exp/annotating/value/value.cc: New. + * mln/clustering/kmean2d.hh: New. + * mln/fun/p2b/achromatic.hh: New. + * mln/fun/v2v/hue_concentration.hh: New. + * mln/fun/v2v/rgb_to_achromatism_map.hh: New. + * mln/fun/v2v/rgb_to_hsv.hh: New. + + 2010-07-07 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Import regional maxima sources from milena green's sandbox. diff --git a/scribo/sandbox/green/README.green b/scribo/sandbox/green/README.green index 7886914..5d6e147 100644 --- a/scribo/sandbox/green/README.green +++ b/scribo/sandbox/green/README.green @@ -685,21 +685,6 @@ rechercher dans les derniers répertoires mp00042c et mp00307c. * doc/labeling/mp00307c: Test sur la representativité des couleurs trouvées. -Faire le changelog, il y a de l'aide dans git/doc/Changelog -... et commiter - - -XI ANNOTATING --------------- - -==> to do - - - -* doc/annotating - - - XI AUTRES ASPECTS DOCUMENTAIRES ------------------------------- @@ -732,3 +717,495 @@ l'effort. * doc/formulae: LaTex directory. * doc/quick_tour: LaTex directory. + +XII ANNOTATING +-------------- + +Tout d'abord, voici les notes documentaires qui ont été réalisées sur +la problématique d'annotation d'image. On trouvera un fichier +class.txt qui a pour but de poser quelques réflexions sur les types de +classes de document. Les informations ne sont pas abouties mais +permettent de défricher un peu le terrain. Le document +syntheseMillet2008 est un compte-rendu de lecture des parties +relatives à nos travaux dans la thèse de Millet. Pour bien comprendre +mon travail, il est impératif de lire les travaux de Millet, ou +simplement ce compte-rendu. Le dernier document est moins intéressant, +il s'agit d'une note de travail sur les indicateurs de Millet bruts de +fonderie (testMillet2008). Cette note n'est ni achevée, ni +aboutie. Elle conclue sur le fait que les indicateurs et les seuils +donnés par Millet dans sa thèse sont complètement à revoir pour nos +besoins. Notemment, des tests sur la détection des images par rapport +aux cliparts nous ont convaincu que certaines images AFP contiennent +des de grandes zones homogènes qui induisent des erreurs dans les +prédicteurs de Millet. Néanmoins, les cas particuliers qui +contredisent les aspects opérationnnels de Millet n'enlèvent pas sa +réflexion: Les images noir/blanc (ou monochromes) ont une très faible +saturation, et/ou une forte concentration de la teinte. Les cliparts +ont une forte concentration de niveaux de gris autour d'un pic. + +* doc/annotating: La documentation relative à l'annotation. + + +Après la lecture des descripteurs de Millet, un des premiers réflexe a +été d'en implémenter plusieurs pour voir ce qu'ils pouvaient ressortir +sur les images que nous avions. Le descripteur BIC sépare une image en +deux ensembles de pixels, les points intérieurs et les points +extérieurs. Les points intérieurs ont la propriété d'être de même +couleur que leur 4-voisins. Attention, la couleur est évaluée dans un +espace RGB à 3 bits. + +* demo/annotating/bic: Histogrammes des points intérieurs et extérieurs. + + +Le descripteur LEP, lui, propose de seuiller par sobel l'image et de +faire l'histogramme des configurations du voisinage des points +seuillés. Pour ce faire, on utilise une convolution un peu spéciale +qui va attribué un unique id en fonction du voisinage. + +* demo/annotating/lep: Histogramme des configurations des voisinages des pixels. + + +Un autre descripteur simple est le nombre de couleur dans une +image. Pour cela, il est possible de construire l'histogramme des +couleurs et de compter les cellules pleines. On peut éventuellement +appliquer une quantification sur l'espace des couleurs. La +compilation laisse place à d'étranges warnings sur une comparaison +entre entiers signés et non signés, mais je n'ai pas la main dans mon +code (ou je ne sais pas comment faire) pour enlever ces warnings. Ils +sont récents, je n'avais pas souvenir de les avoir eu. + +* demo/annotating/nb_color: Compte le nombre de couleurs dans une image. +* exp/annotating/nb_color: Adaptation pour fonctionner sur une base d'image. + + +L'histogramme RGB-64 est un descripteur simple qui quantifie les +couleurs sur 2 bits et réalise l'histogramme dans cet espace. C'est +bien sûr une classification gros grain, mais ajouté au reste ... La +version RGB-64-9 ajoute une phase de division de l'image en 9 sous +images. De cette manière, l'histogramme RGB-64 est construit sur les 9 +sous images. Pour former le descripteur final, on fusionne les neufs +histogrammes. + +* demo/annotating/rgb_64: Histogramme couleur dans l'espace RGB-64 (2 bits/axe). +* demo/annotating/rgb_64_9: Histogramme RGB-64 sur les 9 sous images. + + +Le descripteur de projection relaté par Millet est particulier. En +premier lieu l'image est sous échantillonnée pour réduire sa dimension +à 100 x 100 de manière à borner la taille des vecteurs obtenus au +final. Puis l'image est seuillée par sobel (threshold = 100). L'image +est d'abord divisée horizontalement en deux. Puis on établit la +projecton perpendiculairement à la séparation de manière à obtenir +deux vecteurs de 100 valeurs chacunes. On recommence l'opération en +divisant maintenant l'image verticalement. L'union des 4 vecteurs +forme le descripteur de projection. L'information condensée dans ces +vecteurs est simplement la répartition des contours de manière +horizontale ou verticale. + +* demo/annotating/project: Répartition des contours horizontaux et verticaux. + + +La reconnaissance des cliparts s'appuie sur une analyse d'histogramme +qui est fournie dans le code suivant. L'idée est de dire qu'une image +de type clipart va être reconnaissable surtout à l'aide de ces +contours. La couleur existe mais est très grossière. Le faitde +dessiner à la main implique de simplifier énormément, de caricaturer, +le remplissage. Du coup, une analyse en niveau de gris de +l'histogramme révèle très peu de nuances. Il peut cependant en avoir +un peu. Néanmoins, il existe des logiciels pour aider à la fabrication +des cliparts qui proposent l'usage de dégradé, ce qui nuit à cette +méthode de reconnaissance. Millet analyze l'histogramme normalisé +(histogramme divisé par son pic) et regarde si son energie ne serait +pas concentrée autour du pic (5 pixels de chaque côté +maximum). Parfois cette méthode ne fonctionne pas correctement sur des +photographies qui ont un cadre uniforme. La méthode trouve un pic (le +cadre) et vérifie alors qu'une proportion non négligeable des pixels +sont bien autour de ce pic (tout dépend de l'épaisseur du cadre). Pour +palier à cet inconvénient, Millet propose d'utiliser ce test, non plus +sur l'image entière, mais sur chacune des 16 sous images après un +découpage géométrique régulier. De facto, la contribution du cadre +diminue suffisemment pour repasser en dessous du seuil de +reconnaissance. + +* demo/annotating/stddev_color: Descripteur utilisé reconnaitre des cliparts. +* exp/annotating/stddev_color_16: Adaptation pour le travail sur base. +* demo/annotating/stddev_color_16: Descripteur pour cliparts avec 16 imagettes. +* exp/annotating/stddev_color: Adaptation pour le travail sur une base d'image. + + +A partir de maintenant, tous les morceaux de codes réalisés préparent +directement ou indirectement le résultat de la classification des +bases (exp/annotating/bench). + +Plus de temps pour faire le code use correspondant aux fichiers +librairies. Tout le code pour le faire est dans hsv. Rien de +compliqué, mais allons à l'essentiel. + +Le but du code HSV est d'effectuer les tests de Millet ou des +améliorations sur ces tests. Le premier test proposé par Millet est +l'achromaticité. Il s'agit de regarder s'il existe une faible +variation entre les trois canaux (R/G/B) pour un grand nombre de +pixels. Si c'est le cas, c'est que l'image est presque en niveau de +gris et peut être remplacée facilement par une image grisée sans trop +de distorsions au niveau de la couleur. Nous avons essayer de +généraliser un peu le test de manière à produire, non pas seulement +une réponse sur l'achromaticité de l'image, mais aussi avoir une vue +d'ensemble (sous forme d'image) des variations entre les cannaux pour +chaque pixel. Il n'est pas utile d'analyser les différences sur chacun +des canaux, prendre la différence absolue entre le canal min et le +canal max suffit pour définir le test d'achromaticité. + +Les autres tests de Millet sont la faible saturation et la dominance +de la teinte. Pour savoir si la saturation est faible, il faut +utiliser un histogramme du canal dédié à la saturation. Si 95% des +pixels sont en dessous de 100, alors l'image est faiblement saturée, +elle est en noir et blanc. De la même manière, on regarde la dominance +de la teinte. Pour ce faire, il faut voir si l'histogramme de la +teinte ne possède pas un pic avec une très faible variance (tout +rapproché autour du pic). Si c'est le cas, la dominance de la teinte +est avérée et l'on peut calculé la couleur dominante. L'image est en +niveau de gris, mais colorisé autour d'une couleur (par cepia, vert +...). + +Ce programme est très très sujet à changements, il m'a servit de test +et je ne peux pas juré qu'il est complètement sain au niveau du +traitement. D'autres versions sont potentiellement plus +stables. Notemment celle dans exp/annotating/hsv. + +* mln/fun/v2v/rgb_to_achromatism_map.hh : Distance pour l'achromaticité. +* mln/fun/v2v/achromatic.hh : Define the achromatic map. +* mln/fun/v2v/hue_concentration.hh : Define the distance hue/peak map. +* mln/fun/p2b/achromatic.hh : Say if a site is achromatic. +* demo/annotating/hsv: Code des différents tests de Millet. + + +Dans le répertoire exp/annotating/hue, on trouve 3 fichiers textes qui +rassemblent des classes d'images. Tout d'abord les images ICDAR +n'ayant que du texte et des traces de couleurs (lettrine de couleur, +trait, petit bandeau), un fichier où il n'y a que du texte noir & +blanc et un fichier contenant les images couleurs (avec photographies +ou dessins). Cette classification a été effectuée de manière manuelle. +Le code hue test la proportion de pixels étant autour du pic de +teinte. Il s'agit de savoir si la dominance d'une teinte est +avérée. Le code renvoit la proportion de pixels agglomérés autour du +pic. Le but est de généralisé les tests de millets pour qu'ils se +ressemblent le plus possible. + +* exp/annotating/hue: Implémentation de la généralisation du test de Millet. +* mln/fun/v2v/rgb_to_hue_map.hh : Construction de la map de teinte. + + +On retrouve les trois fichiers permettant de classifier la base ICDAR +en trois sous populations. Le test de saturation consiste simplement à +regarder si une certaine quantité de la population de l'histogramme de +saturation est en dessous d'un certain seuil. La généralisation du +test ne porte pas sur le test en lui-même, mais sur la forme dans +lequel le test est fait. + +* exp/annotating/saturation: Implémentation de la généralisation du test. +* mln/fun/v2v/rgb_to_saturation_map.hh : Construction de la map de saturation. + + +Le test de value a déjà été décrit précédemment dans +stddev_color. L'idée est toujours la même, mais cette fois il est +effectué dans l'espace des valeurs (HSV). Cela ne change pas grand +chose, puisqu'il était utilisé sur des images en niveau de gris. C'est +l'un des tests importants car il réagit à la différentiation entre une +image type photographie et une image plus stylisée comme un +clipart. Ce test a aussi des vertus pour la distinction entre du noir +& blanc et de la couleur. Il s'avère que les images type photographie +avec pleins de couleurs ont un histogramme moins sujet aux pics que +les histogrammes noir & blanc. De facto, l'énergie de l'histogramme +est distribué sur l'ensemble de la plage contrairement aux images noir +& blanc où il y a une concentration de chaque côté de l'histogramme +(bipolarité). + +* exp/annotating/value: Implémentation de la généralisation du test de Millet. +* mln/fun/v2v/rgb_to_value_map.hh : Transformation d'espace. + + +Le programme hsv reprend les tests préalablement élaborés auparavant +sur les plans H, S puis V. Il combine tout en un seul programme pour +avoir une vision plus synthétique de ce qui se passe sur les 3 espaces +simultanément. + +Les tests incorpore mes transformations. C'est à dire que l'on +effectue une série de test équivalent à ceux de Millet (au moins dans +l'idée et le plus souvent, il s'agit d'une réecriture sous une autre +forme) en partant des histogrammes normalisés. Un histogramme +normalisé est un histogramme de flottant qui contient l'histogramme +classique divisé par le nombre total de pixels. En faisant cela, on se +déplace dans l'espace des distributions. Si l'histogramme n'a qu'un +seul pic et que ce dernier contient tous les pixels (pic de dirac par +exemple), alors l'histogramme normalisé donnera 1 comme valeur de +proportion à ce pic. En fait, l'intégrale d'une distribution vaut +1. Le but des distributions est de s'affranchir des caractéristiques +de taille de l'image, ce qui permet de comparer les histogrammes entre +eux. On peut ainsi comparer les histogrammes d'une photo AFP petit +format avec celui d'une image grand format de la base ICDAR. Le but du +jeu est de garder les idées de Millet dans ce nouvel espace. Les tests +obtenus sont équivalents mais pas identiques. + +Pour la teinte, une fois l'histogramme normalisé obtenu, on cherche à +savoir s'il existe un pic avec une forte proportion de l'histogramme +autour de ce pic. Plutôt que le pic, nous prenons la moyenne qui est +un opérateur un peu plus robuste et nous calculons la proportion de +l'histogramme autour (un seuil donné en paramètre défini le périmètre +du calcul). L'expression sous forme de proportion plutôt que sous la +forme d'une variance rend le test homogène avec les autres tests. + +Pour la saturation, après normalisation, on regarde la proportion de +l'histogramme en dessous d'un certain seuil. Il n'y a pas de +changement significatif par rapport au test de Millet. Le seuil +initialement proposé par Millet était 100, il a été adapté car nos +bases sont différentes des siennes (le notre est à 25). + +Le test sur les valeurs (cliparts contre photos) a vraiment un intérêt +au delà de ce cas d'utilisation. Il nous renseigne sur la +concentration autour d'un pic de l'histogramme ou non. Nous préferrons +la mesure de similarité à l'équi-répartition des densités, mais l'idée +est exactement la même. Est-ce que notre histogramme est plat ou +inversement, est-ce que les contributions sont rassemblées autour d'un +pic? Si une image est noir et blanc, il existera un pic correspondant +au fond et la densité s'éloignera fortement de la distribution +équiprobable. Dans le cas maintenant d'une image couleur, la +répartition est plus homogène, couvrant un large spectre dans le +domaine de l'histogramme. Du coup, la distribution semblera davantage +equi-répartie. On notera que nous prenons le test à l'envers de ce que +propose Millet. Il essaye de voir si il y a un pic et calcule une +forme de contribution normalisée autour de ce pic. Nous au contraire, +on regarde l'absence de pic et on calcule la différence entre la +densité et cette absence de pic. Notre avantage par rapport à Millet +est démontré particulièrement dans les cas où il existe plusieurs +grosses distributions. A contrario, notre test souffre des cas où il +existe de nombreuses petites distributions. + + +Voici un premier retour sur les expériementations: +La discrimination entre la base AFP et la base ICDAR peut se faire en +étudiant la forme des densités des niveaux de gris. Les images +naturelles semblent avoir un spectre recouvrant en général les 256 +niveaux de gris alors que les images de documents ont une présence +importante du fond. Dans le cadre d'une densité, ce qui est alloué sur +le fond ne peut se retrouver ailleurs. Une comparaison avec la densité +équiprobable nous renseigne donc sur la nature des images. Il semble +néanmoins qu'un certain nombre d'images défient ce dispositif. Par +exemple des gros plans sur des zones mono-teintée (ski, voile,site +web). + +* exp/annotating/hsv: Code unifiant les trois tests sur chacun des plans HSV. +* mln/fun/v2v/rgb_to_hue_map.hh : Transformation d'espace. +* mln/fun/v2v/rgb_to_saturation_map.hh : Transformation d'espace. +* mln/fun/v2v/rgb_to_value_map.hh : Transformation d'espace. + + +Le test sur l'achromatisme des images est décrit dans le code +suivant. Il a été purement et simplement abandonné dans la mesure où +c'est un cas très particulier qui est repris par une saturation très +faible. La saturation s'exprime comme 1 - min(channel)/max(channel), +mais dans le cas où le min(channel) == max(channel), la saturation +vaut 0. Le problème vient plutôt du calcul de la teine qui ne peut pas +admettre que le min soit égal au max. Pour ce calcul, on se débrouille +pour gérer le cas et renvoyer une valeur standardisée, Millet +proposait -1. + +* exp/annotating/achromatism: Détection d'image couleur en niveau de gris. +* mln/fun/v2v/rgb_to_achromatism_map.hh : Transformation d'espace. + + +Ce programme a pour but de créer les histogrammes normalisés en rafale +pour les bases AFP et ICDAR. Il assure leur création dans les six +plans possibles R,G,B,H,S,V. De cette manière il est possible de +vérifier les corrélations éventuelles entre le canal B et G. Par +ailleurs, après visionnement de tous les histogrammes, on note des +spécificités dans les deux bases sur les plans S et V. Certaines +images ont des réactions très fortes sur le plan H, mais ce n'est pas +une caractéristique pour une base (seulement pour ces images en +question). Les résultats ont déjà été sauvegardés dans le répertoire +image du LRDE. Des infos sont notés à ce sujet dans README.result. Le +seul but de ce calcul est de maîtriser csujet dans README.result. Le +seul but de ce calcul est de maîtriser ce qui se passe dans ces +espaces, pas simplement de supposer ce qu'il pourrait s'y passer. La +comparaison des images est rendu possible quelque soit la base +d'origine par le fait qu'elles sont normalisées. Chaque fréquence des +histogrammes est divisée par la somme des fréquences. On obtient donc +une version discrète des densitées. La somme des nouvelles fréquences +vaut 1. On voit davantage si les densités se rapprochent d'une +équi-répartition ou non. La négative implique un ou plusieurs pics. Le +cas défavorable dans mon approche (qui existe pour certaines images) +est une multitude de tout petits pics autour de l'équi-répartition. Ce +n'est pas équi-répartie pour les calculs mais ce n'est clairement pas +la manisfestation d'une concentration de l'énergie quelque part. + +* exp/annotating/histo: Creation des histogrammes normalisés. +* mln/fun/v2v/rgb_to_hue_map.hh: Transformation d'espace. +* mln/fun/v2v/rgb_to_saturation_map.hh: Transformation d'espace. +* mln/fun/v2v/rgb_to_value_map.hh: Transformation d'espace. + + +Dans le programme erreur, on teste l'idée de jonathan. Une référence à +ce sujet peut être trouvée dans README.img. Les bases AFP et ICDAR +sont retravaillées à l'aide de ImageMagick et de Gimp pour diminuer le +nombre de couleurs initiales. On oblige à n'avoir que 30, 20 ou 10 +couleurs pour chaque base. Il y a donc 4 versions de chacune des bases +en comptant la version originale des images. Les algorithmes utilisés +par Gimp et ImageMagick sont très différents. L'idée de Jonathan était +de dire que lorsqu'une image est couleur, plus on réduit le nombre de +couleur, plus elle change vis-à -vis de l'image originale. Inversement, +si une image possède peu de couleurs (noir & blanc), sa dégradation ne +l'altérera pas tant que cela. Prenons une image d'un skieur de la base +AFP, en réduisant le nombre de couleurs, la combinaison marron va +devenir marron strictement homogène et la perception que nous en avons +est visuellement très altérée. A contrario, une image en noir & blanc +ne semble pas bouger d'un pouce. La comparaison avec la discrimination +sur la saturation ou la valeur montrera des résultats un peu meilleur +plus tard mais il impossible de préjuger a priori des résultats +futurs. L'erreur entre l'image initiale et l'image dégradée est +calculée avec le PNSNR (compression p278 Handbook Color). Le programme +calcule la dégradation et essaie de trouver automatiquement des seuils +de séparation pour les deux bases. 4 détection de seuils sont testées. +Ces seuils sont calculées avec deux bases représentatives des bases à +discriminer. En mode production, le seuil est vu comme une constante +pour le programme. Les deux premières classifications renvoient un +seuil calculée comme une moyenne pondérée des moyennes des populations +à discriminer. Le premier détecteur pondère par la déviation standard +et le second par la variance. Le troisième simule deux populations +gaussiennes de variances différentes et résoud l'équation de second +degré qui en résulte. Enfin, le dernier test ne préjuge pas des +distributions statistiques et réalise la minimisation de l'erreur de +classification. Pour cela, il suffit de compter le nombre d'images +bien classées et mal classées sur les bases d'apprentissage des +populations. On utilise pour cela la méthode Otsu. Le détecteur PNSRN +renvoie une valeur. Pour chaque population (AFP, ICDAR), on construit +l'histogramme de des valeurs obtenues. A noter, qu'il faut que les +valeurs du PNSNR soient ramenées entre 0 et 255. En pratique ce n'est +pas un problème, mais il faut le mettre en oeuvre, dans mon code, ce +n'est pas fait (les valeurs ne posaient pas de problème). Donc, pour +chaque seuil possible (de 0 à 256), on étudie la matrice de classement +(groupe 1 - détecté groupe 1, groupe 1 - détecté groupe 2, groupe 2 - +détecté groupe 1, groupe 2 - détecté groupe 2). L'erreur est +simplement la somme des quantités groupe x - détecté groupe y avec x +!= y. Finalement, pour chaque seuil, il est possible de connaitre +l'erreur. Le processus de minimisation de l'erreur revient à chercher +le seuil associé à l'erreur minimale. C'est ce dernier test que nous +préconisons. Il faut mettre en lumière que ces quatre tests renvoient +à peu près les mêmes seuils. La minimisation de l'erreur étant le +meilleur de tous puisqu'il minimise directement l'erreur de +classification et c'est ce que nous cherchions en fin de compte. Un +cinquième test a été fait avec une analyse de fisher sur l'histogramme +des populations mélangées, mais c'est une idée saugrenue car nous +disposons à ce niveau des populations séparées et évidemment les +résultats sont moins bons. + +Petit rappel. Un vieux problème a été mis à jour ici. Lorsqu'on +calcule des informations sur un histogramme, le type temporaire qui +contient les résultats ne doit pas être le type qui encode les valeurs +des résultats. Par exemple, un histogramme de byte, un calcul de +variance tout simple où il faut stocker la valeur des pixels au carré +multiplié par leur occurrence tiendra facilement dans un long mais pas +dans un byte. Le sucre avec les acesseurs des itérateurs tend à +mélanger les genres. Une solution simple est de stocker la valeur de +l'histogramme dans un type pouvant effectuer les calculs et ensuite +retravailler avec ce type. Nous avions déjà discuté de ce problème et +je ne sais pas si tu avais pu corriger le problème. Si la +classification renvoit n'importe quoi, il se peut que cela provienne +de ce problème. Le problème ne se voit pas sur des calculs de +moyennes, il ne s'observe que sur certains calculs de variance (dépend +de la formule utilisée). + +* exp/annotating/error: Test de l'idée de jonathan (dégradation des couleurs) + + +Le travail dans bench.cc reprend toute sorte de travaux déjà réalisés +dans d'autres fichiers. Il a pour but de comparer ensemble tous les +descripteurs pour la reconnaissance de base de données entre l'AFP et +l'ICDAR. + +Le travail commence avec un certain nombres de routines travaillant +sur les histogrammes permettant de trouver le pic, la moyenne, la +variance et d'autres éléments. Les routines étaient éparpillées dans +le code, du coup je les ai regroupées au même endroit. Il y a des +redondances de code correspondant à des copier/coller ou à différents +essais. Prendre les versions les plus génériques et en faire des +accumulateurs serait un riche idée. + +Huit détecteurs sont comparés ensembles: +- hue1, détection d'un pic de teinte très dense par la méthode de Millet. +- sat1, détection d'une densité importante dans les saturations basses (Millet). +- lvl0, comptage du nombre de niveau de gris (idée de Millet). +- hue0, détection d'un pic de teinte par la méthode des densités. +- sat0, détection d'une forte basse saturation par la méthode des densités. +- val0, détection d'un pic de niveau de gris par la méthode des densités. +- val1, détection d'un pic de niveau de gris par la méthode de Millet. +- err, PNSNR (idée de jonathan). + +FIXME: Attention, le PNSNR n'est pas borné, il faut le faire, il doit avoir +au maximum 255 comme valeur. + +Tous les détecteurs ont été expliqués en large, en long et en travers +dans les sources précédentes. LIRE le chapitre XII (ANNOTATING) de ce fichier +en entier. + +Ensuite vient les séparateurs de population statistiques qui ont été +déjà introduit dans le fichier error. Bien que tous soient présent, +c'est la minimisation de l'erreur qui est utilisée. + +Enfin vient le front end, l'un des plus complexes que j'ai écrit cette +année. Le main lui même, n'est pas la partie la plus complexe. Il +définit les actions fonctionnelles à réaliser et la structure de +donnée réalisée pour garder la trace de tous les résultats. +- File_name : Cette variable retient le nom des fichiers. C'est un tableau +de la taille du nombre d'images rencontrées toute base confondue. Comme on +ne parcourt qu'une fois les répertoires, il faut pouvoir retenir cette +information qui servira éventuellement pour la nomenclature des dumps +par la suite. +- Result : Cette variable contient toutes les informations du traitement. +C'est un tableau à deux dimensions, une pour les images et une autre pour +les descripteurs. L'index d'image est le même que pour file_name. +- Size : Cette variable contient le nombre d'images par database. +- Threshold : Cette variable va stocker les seuils calculés sur chacun +des descripteurs. Ces seuils sont sensés effectuer la séparation entre les +deux bases. +- Cxx : variables de comptage des images bien ou mal classées relativement +à la position des seuils et à l'appartenance d'origine des images. Cette +appartenance d'origine est calculée à l'aide Size qui contient le nombre +d'image par base de données. Comme les images sont vues dans l'ordre, les X +premières appartiennent à la base ICDAR et les Y suivantes à la base AFP. +- histo : Variable servant à effectuer par database et par descripteur un +histogramme utilisé ensuite lors de la discrimination des bases. + +La partie compute_descriptors a pour mission de passer une fois sur +toutes les images et de calculer les descripteurs associés. A l'issue +de cette passe file_name, result et size sont remplis et pourront être +utilisés par les autres routines. + +La partie dump_descriptors a pour but de réaliser un tracé en gnuplot, +avec une couleur différente pour chaque base. Le graphe montre les +valeurs utilisées par chaque descripteur pour toutes les images de la +base. + +La partie correction des descripteurs est optionnelle et force un +certain nombre de valeurs pour être inférieures à 256. Le but était de +pouvoir travaillé, même si les descripteurs n'étaient pas complètement +opérationnels. Elle sert donc pour le debug. + +La partie calcul des histogrammes sert à obtenir la distribution des +valeurs de chacun des descripteurs en fonction des bases +utilisées. C'est une étape préliminaire à l'analyse des populations et +au calcul des seuils. + +La partie calcul des seuils repose sur la minimisation des erreurs de +classification. Cette méthode ne fonctionne bien si la classe zéro à +une moyenne inférieure à la classe un. Du coup, il n'est pas possible +de déterminer de manière simple si la classe 0 correspond à l'AFP ou à +l'ICDAR. Pour chaque descripteur, les cartes sont rebattues. + +Enfin, tous les histogrammes sont sauvés pour pouvoir comprendre et +visualiser les résultats. + +Le main2 ne sert à rien, juste pour des essais. + +Attention, modification VAL0/VAL1, vérifiez que le bon descripteur est +au bon endroit dans le tableau. + +* exp/annotating/bench: Comparaison des détecteurs pour la classif. ICDAR/AFP. \ No newline at end of file diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/bic/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/bic/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/bic/bic.cc b/scribo/sandbox/green/demo/annotating/bic/bic.cc new file mode 100644 index 0000000..f63d260 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/bic/bic.cc @@ -0,0 +1,122 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet BIC descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It quantifies the three channels on 3 +/// bits, then builds two histograms with the interior pixels and the +/// exterior ones. The interior pixels are those where color value is +/// the same as their 4 neighbouring pixels. The descriptor is the +/// fusion of the two histograms. + +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/var.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> +#include <mln/data/fill.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/colors.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Main entry. +/// +/// Load the images, compute the interior and the exterior with the +/// predicate, then compute the two histograms separately. +int main() +{ + typedef mln::value::rgb<3> t_rgb3; + typedef mln::value::rgb8 t_rgb8; + typedef mln::neighb2d t_neighb2d; + typedef mln::image2d<t_rgb3> t_image2d_rgb3; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::fun::v2v::rgb8_to_rgbn<3> t_rgb8_to_rgb3; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb3 input_rgb3; + t_image2d_rgb3 interior_rgb3; + t_image2d_rgb3 exterior_rgb3; + t_histo3d histo_exterior; + t_histo3d histo_interior; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb3 = mln::data::transform(input_rgb8, t_rgb8_to_rgb3()); + + mln::initialize(interior_rgb3, input_rgb3); + mln::initialize(exterior_rgb3, input_rgb3); + + mln::data::fill(interior_rgb3, mln::literal::black); + mln::data::fill(exterior_rgb3, mln::literal::black); + + mln_piter_(t_image2d_rgb3) p(input_rgb3.domain()); + mln_niter_(t_neighb2d) n(mln::c4(), p); + + for_all(p) + { + bool is_interior = true; + + for_all(n) + is_interior = is_interior && (input_rgb3(p) == input_rgb3(n)); + + if (is_interior) + interior_rgb3(p) = input_rgb3(p); + else + exterior_rgb3(p) = input_rgb3(p); + } + + histo_interior = mln::data::compute(t_histo3d_fun(), interior_rgb3); + histo_exterior = mln::data::compute(t_histo3d_fun(), exterior_rgb3); + + //mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo_interior); + mln::debug::println(histo_exterior); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/hsv/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/hsv/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/hsv/hsv.cc b/scribo/sandbox/green/demo/annotating/hsv/hsv.cc new file mode 100644 index 0000000..e87ab2d --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/hsv/hsv.cc @@ -0,0 +1,721 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet HSV operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to HSV +/// one. Formulae are classical, we can find them on the web. +/// +// Val = max(R,G,B). +// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +/// +/// \fixme: This code is unstable. It was using to make a lot of tests. +/// Don't use it, we have more stable version of it. +#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> + + +/// \brief Classify a rgb a value with a color of reference. +/// +/// \param[in] rgb a rgb8 value. +/// +/// \return a rgb8 initialize with the selecting literal. +/// +/// This classification of the RGB color space is based on the +/// Millet's work. It uses the hue wheel to do it and make new +/// distinction as brown/orange for instance. +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; +} + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +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; +} + + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +// 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) + + +/// \brief Decide if an image is achromatic or not [Millet]. +/// +/// \param[in] input_rgb8 the input image. +/// \param[in] threshold the distance threshold used for equality. +/// \param[in] percentage the percentage of pixels that very the test. +/// +/// This is an improving of the Millet test. The original test +/// compares rgb values to each other and look at differences greater +/// than the threshold. If the number of pixel that pass the test are +/// greater than 99.0, then the image is declared achromatic. In fact, +/// there is few variations between the three channels, so we can say +/// that it is like a grey image. We can with no too distortions +/// replace the color image by the grey one. The improving is in first +/// creating a map of the difference. As we can't keep the free +/// differences between channels, we look at reducing the test and we +/// find an equivalent one based on the difference between minima +/// channel value and the maxima channel value. After the map is +/// ready, we make binarization with the threshold. Then we compute +/// the histogram 1d for every pixels of the map that are greater the +/// threshold. Then, we count pixels in the histogram and traduce the +/// count in percentage to compare to the second threshold. Details +/// are saved in files and printed in the screen. +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"); +} + + +/// \brief Decide if an image is low saturate or not and so b/w [Millet]. +/// +/// \param[in] input_hsvf the input image in the HSV space. +/// \param[in] achromatism_mask the mask to prevent computing bad saturation. +/// \param[in] threshold the distance threshold used for equality. +/// \param[in] percentage the percentage of pixels that very the test. +/// +/// As we are in the HSV space, we can just isolate saturation +/// channel. The original idea of Millet is to build histogram bound +/// to 256 for the saturation. If a great percentage (95%) of the +/// population are under a threshold (100), then the image has got low +/// saturation. So, it is b/w image. Low saturation is sometimes +/// redundant with the achromatism test but not every time. + +// 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); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), + view|(mln::pw::value(achromatism_mask) == true)); + prop = (100.0 * (cnt2 - cnt1) / cnt2); + result = (prop > percentage); + + std::cout << "Saturation" << std::endl; + + cnt1 = count_histo(histo | mln::box1d(mln::point1d(0),mln::point1d(100))); + cnt2= mln::geom::nsites(achromatism_mask| + (mln::pw::value(achromatism_mask)==false)); + + std::ofstream txt_stream("saturation.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, "saturation.pgm"); + mln::io::plot::save_image_sh(histo, "saturation.sh"); + mln::io::pbm::save(mask, "saturation.pbm"); +} + +/* This is a classification of the ICDAR base in 3 modalities. +// 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"); +*/ + + +/// \brief The main entry and the whole processing routine +/// +/// This is the routine which works on Millet test. First compute the +/// achromatic mask. Then build the transfer between RGB and HSV. Then +/// isolate each channel to work on it. Each separate channel is bound +/// between 0 and 255. Then keep only pixels low saturated (< 100 in S +/// canal) in a specefic map. Build the 3 histograms without the +/// achromatic pixels based on the 3 separated channels (H/S/V). Build the +/// 3 maps (hue_concentration, low_saturation and achromatism). Then do +/// the 3 tests of Millet with the thow thresolded pass (threshold,percentage). +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 of achromatic pixels ??? + 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); + + // IDENTIFY LOW SATURATED PIXELS + 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)); + + + // Study the maps + 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) ACHROMATISM + 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) LOW 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) HIGH HUE CONCENTRATION + 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; + + return 0; +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/lep/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/lep/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/lep/lep.cc b/scribo/sandbox/green/demo/annotating/lep/lep.cc new file mode 100644 index 0000000..981a369 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/lep/lep.cc @@ -0,0 +1,127 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet LEP descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works on grey level image. First, +/// Sobel is applied on the image. Then a specific window is build to +/// be convolved on the image. The aim of the convolution is to mark +/// the configuration of the neighbouring for this thresholded image. +/// Histogram of the configuration is then created and that's all. + +#include <iostream> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/w_window2d_int.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/linear/ch_convolve.hh> +#include <mln/linear/convolve.hh> +#include <mln/linear/sobel_2d.hh> + +#include <mln/make/w_window2d.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> + +#include <mln/core/var.hh> + +/// \brief Main entry. +/// +/// Load the pgm image. Threshold it. Identifie the pixel +/// configuration with specific convolution filter. Build 1d histogram. + +int main() +{ + using namespace mln; + + typedef mln::w_window2d_int t_win2d; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::int_u<9> t_int_u9; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_int_u9> t_image2d_int_u9; + typedef mln::image2d<int> t_image2d_int; + typedef mln::image2d<unsigned> t_image2d_unsigned; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<double> t_image2d_double; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + + t_image2d_int_u8 input_int_u8; + t_image2d_int input_int; + t_image2d_unsigned input_unsigned; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + t_image2d_float conf_float; + t_image2d_int_u9 conf_int_u9; + t_histo1d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,ICDAR_50P_PGM_IMG_PATH"/mp00082c_50p.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(input_int_u8); + + //mln::io::plot::save_image_sh(sobel, "sobel.sh"); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(mln::value::int_u8(), thresh_bool); + //mln::io::plot::save_image_sh(thresh, "thresh.sh"); + + int ws[] = { 1, 2, 4, + 8, 256, 16, + 32, 64, 128 }; + + t_win2d win2d = mln::make::w_window2d(ws); + conf_float = mln::linear::convolve(thresh_int_u8, win2d); + conf_int_u9 = mln::data::convert(t_int_u9(), conf_float); + histo = mln::data::compute(t_histo1d_fun(), conf_int_u9); + + mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/nb_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/nb_color/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc b/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc new file mode 100644 index 0000000..acc64e9 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc @@ -0,0 +1,143 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Compute the number of colors in an image. +/// +/// Build histogram of colors and count the bins different from zero. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/math/sum.hh> +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo3d_rgb.hh> +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/variance.hh> + +#include <mln/algebra/vec.hh> + +#include <mln/arith/diff_abs.hh> + +#include <mln/core/macros.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/labeling/regional_maxima.hh> +#include <mln/labeling/mean_values.hh> +#include <mln/labeling/compute.hh> + +#include <mln/literal/colors.hh> + +#include <mln/morpho/opening/volume.hh> +#include <mln/morpho/elementary/dilation.hh> + +#include <mln/opt/at.hh> + +#include <mln/pw/cst.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/rgb.hh> +#include <mln/value/int_u.hh> + +/// \brief Count the colors. +/// +/// \param[in] image the name of the image to process. +/// +/// \return the number of colors. +/// +/// Count the color by building histogram. Quantification is done to +/// reduce the size of the histogram. +/// +/// \fixme: Strange compilation warning, I don't know how to solve it! + +// n < 8, n is the degree of quantification +template <unsigned n> +unsigned count_image_color(const std::string& image) +{ + 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_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + 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; + typedef mln::accu::meta::math::count t_count_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + t_histo3d histo; + t_histo3d opened; + + mln::io::ppm::load(input_rgb8, image.c_str()); + +// unsigned nb_pixel = input_rgb8.ncols() * input_rgb8.nrows(); +// unsigned min_volume = (unsigned)(nb_pixel * 0.054); + unsigned nb_color = 0; + + input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + histo = mln::data::compute(t_histo3d_fun(), input_rgbn); + nb_color = mln::data::compute(t_count_fun(), + (histo | (mln::pw::value(histo) != 0)).rw()); + + return nb_color; +} + + +/// \brief Main entry. +/// +/// Print the number of colors. +int main() +{ + unsigned val=count_image_color<8>(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + + std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/project/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/project/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/project/project.cc b/scribo/sandbox/green/demo/annotating/project/project.cc new file mode 100644 index 0000000..4cab73c --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/project/project.cc @@ -0,0 +1,275 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet PROJECT descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works a grey level image. First, +/// we make subsampling of the image to the size 100x100. Then Sobel +/// is applied. After that, we divide the image in 2 sub-images with +/// horizontal or vertical splitting. Projection (summing along a +/// direction) is done in a way that preserves a vector of size +/// 100. Finally, it results 4 vector of size 100 which are concatened +/// to build the descriptor. + +#include <iostream> +#include <sstream> + +#include <mln/accu/image/init.hh> +#include <mln/accu/image/take.hh> +#include <mln/accu/image/to_result.hh> +#include <mln/accu/stat/mean.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/image/dmorph/unproject_image.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/projection.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/pbm/save.hh> + +#include <mln/linear/sobel_2d.hh> + +#include <mln/opt/at.hh> + +/// \brief get the sub-image name. +/// +/// \param[in] base the common part of the name. +/// \param[in] i the column. +/// \param[in] j the line. +/// +/// \result the complete name of the sub-image. +/// +/// This function builds the complete name of a sub-image by making +/// explicit its position in the array. +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + + +/// \brief Project row data. +/// +/// \param[in] img the binary image converted to u_int8. +/// +/// \ return a vector. +/// +/// This routine "eats" one dimension of the image by making a +/// projection. The projection sums the pixel along a specific +/// direction. It results a vector. +mln::image1d<mln::value::int_u8> +project_row(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,0> t_projection; + + mln::image1d<t_sum> img_accu(img.ncols()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + + +/// \brief Project column data. +/// +/// \param[in] img the binary image converted to u_int8. +/// +/// \ return a vector. +/// +/// This routine "eats" one dimension of the image by making a +/// projection. The projection sums the pixel along a specific +/// direction. It results a vector. +mln::image1d<mln::value::int_u8> +project_col(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,1> t_projection; + + mln::image1d<t_sum> img_accu(img.nrows()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + + +/// \brief The image processing routine. +/// +/// Describe the processing chain. First subsample the input image to +/// 100x100. This is done by splitting the image and then compute the +/// mean of the subimage. The result is a pixel of the subsample +/// image. Then apply Sobel and threshold. Then split in two +/// (vertically or horizontally), it results 4 images. Finally, make +/// the projection. +int main() +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::algebra::vec<3,unsigned> t_vec3u; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::accu::meta::stat::mean t_mean_fun; + typedef mln::accu::math::sum<t_int_u8,t_int_u8> t_sum; + typedef mln::image1d<t_int_u8> t_image1d_int_u8; + + t_image2d_int_u8 input_int_u8; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,ICDAR_50P_PGM_IMG_PATH"/mp00082c_50p.pgm"); + + + // IMAGE SPLITTING PHASE + t_image2d_int_u8 subimg_int_u8(mln::box2d(mln::point2d(0,0), + mln::point2d(100,100))); + + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 100; + unsigned sz_col = (pmax.col() - pmin.col())/ 100; + + std::cout << domain << std::endl; + + // Subsampling in 100x100 + // FIXME Test that is it a subsampling, not a upsampling! + for (unsigned i = 0; i < 100; ++i) + for (unsigned j = 0; j < 100; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_int_u8 input_part_int_u8(dom); + + mln::data::paste(input_int_u8 | dom, input_part_int_u8); + //mln::io::pgm::save(input_part_int_u8, get_name("output",i,j)); + float mean = mln::data::compute(t_mean_fun(), input_part_int_u8); + t_int_u8 val = static_cast<t_int_u8>(mean); + mln::opt::at(subimg_int_u8, i,j) = val; + } + + mln::io::pgm::save(subimg_int_u8, "subimg.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(subimg_int_u8); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(t_int_u8(), thresh_bool); + + mln::io::pbm::save(thresh_bool, "subimg.pbm"); + + // Define img_up, img_down, img_left, img_right + t_image2d_int_u8 img_up(mln::box2d(mln::point2d(0,0), + mln::point2d(49,99))); + + mln::data::paste(thresh_int_u8 | img_up.domain(), img_up); + + + t_image2d_int_u8 img_down(mln::box2d(mln::point2d(50,0), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_down.domain(), img_down); + + t_image2d_int_u8 img_left(mln::box2d(mln::point2d(0,0), + mln::point2d(99,49))); + + mln::data::paste(thresh_int_u8 | img_left.domain(), img_left); + + + t_image2d_int_u8 img_right(mln::box2d(mln::point2d(0,50), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_right.domain(), img_right); + + + // Define project_up, project_down, project_left, project_right + t_image1d_int_u8 project_up = project_row(img_up); + t_image1d_int_u8 project_down = project_row(img_down); + t_image1d_int_u8 project_left = project_col(img_left); + t_image1d_int_u8 project_right = project_col(img_right); + + mln::io::pgm::save(img_up, "up.pgm"); + mln::io::pgm::save(img_down, "down.pgm"); + mln::io::pgm::save(img_left, "left.pgm"); + mln::io::pgm::save(img_right, "right.pgm"); + + // PRINTING PHASE + + std::cout << std::endl << "UP" << std::endl; + mln::debug::println(project_up); + + std::cout << std::endl << "DOWN" << std::endl; + mln::debug::println(project_down); + + std::cout << std::endl << "LEFT" << std::endl; + mln::debug::println(project_left); + + std::cout << std::endl << "RIGHT" << std::endl; + mln::debug::println(project_right); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/rgb_64/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/rgb_64/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc b/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc new file mode 100644 index 0000000..b49f979 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc @@ -0,0 +1,80 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet RGB-64 descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works by making 2 bits +/// quantification on each channel. It result a processing in RGB-64 +/// space. Then we build the color histogram. + +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Main entry. +/// +/// Loading, Quantifiying in two bits each channel, then building +/// color histogram. +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/rgb_64_9/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/rgb_64_9/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc b/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc new file mode 100644 index 0000000..355cbfd --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc @@ -0,0 +1,132 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet RGB-64 descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works by making 2 bits +/// quantification on each channel. It result a processing in RGB-64 +/// space. Then we build the color histogram. + +#include <iostream> +#include <sstream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Get the sub image name. +/// +/// \return the name of the ith/jth sub image of the array. +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + +/// \brief Same code as rgb-64 but divide the wall image in nine. +/// +/// Adding a splitting phase for image rgb-64 code reference. +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_rgb2.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 3; + unsigned sz_col = (pmax.col() - pmin.col())/ 3; + + std::cout << domain << std::endl; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 3; ++i) + for (unsigned j = 0; j < 3; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_rgb2 input_1o9_rgb2(dom); + + mln::data::paste(input_rgb2 | dom, input_1o9_rgb2); + mln::io::ppm::save(input_1o9_rgb2, get_name("output",i,j)); + + histo = mln::data::compute(t_histo3d_fun(), input_1o9_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); + } + +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/stddev_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/stddev_color/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc b/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc new file mode 100644 index 0000000..549834f --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc @@ -0,0 +1,191 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +unsigned stddev_color(const std::string& 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_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + t_histo1d histo; + t_point1d max_site; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + mln::io::pgm::save(input_int_u8, "tmp.pgm"); + mln::io::plot::save_image_sh(histo, "histo.sh"); + 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; +} + + +/// \brief Main entry. +/// +/// Front end for image processing. +int main() +{ +// unsigned val = stdev_color(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + unsigned val = stddev_color(ANNOTATING_1_LOGO_IMG_PATH "/logo06.ppm"); + +// std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/stddev_color_16/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/stddev_color_16/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc b/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc new file mode 100644 index 0000000..98d94fd --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc @@ -0,0 +1,261 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. When photographies +/// has some large uniform border, the detection can fail. To improve +/// the method, Millet decide to subdivise the image in 16 sub images +/// on which it applies the test. +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> +#include <mln/data/paste.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] input_int_u8 the 8 bits input image to analyze. +/// \param[in] name_histo the name of the output histogram. +/// \param[in] name_image the name of the ouput sub image. +/// +/// \return the deviation, but at this time nothing.. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +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; +} + + +/// \brief Divide the image in 16 sub images. +/// +/// \param[in] image the input image. +/// +/// \result nothing. +/// +/// Divive the input image in 16 by uniform and geometrical +/// method. When a sub image is ready, call the stddev routine to show +/// stats on it. +unsigned stddev_color_16(const std::string& 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_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 4; + unsigned sz_col = (pmax.col() - pmin.col())/ 4; + + std::cout << domain << std::endl; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 4; ++i) + for (unsigned j = 0; j < 4; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_int_u8 input_1o16_int_u8(dom); + std::ostringstream name_histo(""); + std::ostringstream name_image(""); + + name_histo << "histo" << i << "_" << j << ".sh"; + name_image << "image" << i << "_" << j << ".ppm"; + + mln::data::paste(input_int_u8 | dom, input_1o16_int_u8); + + stddev_color(input_1o16_int_u8, + name_histo.str().c_str(), + name_image.str().c_str()); + } + + return 0; +} + + +/// \brief Main entry. +/// +/// Just a front end for image processing routine. +int main() +{ +// unsigned val = stddev_color_16(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + unsigned val = stddev_color_16(ANNOTATING_1_LOGO_IMG_PATH "/logo06.ppm"); + +// std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/achromastism/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/achromastism/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc b/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc new file mode 100644 index 0000000..3486b20 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc @@ -0,0 +1,179 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + + +/// \file +/// +/// \brief Implement the Millet achromatism operator [millet.phd.2008.pdf] +/// +/// An image is said to be achromatic when have the RGB channel which +/// are quite identicals. + +#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> + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + +/// \brief Decide if an image is achromatic or not [Millet]. +/// +/// \param[in] input the input image. +/// \param[in] output the histogram of the achromatic map. +/// \param[in] threshold the distance threshold used for equality. +/// +/// This is an improving of the Millet test. The original test +/// compares rgb values to each other and look at differences greater +/// than the threshold. If the number of pixel that pass the test are +/// greater than 99.0, then the image is declared achromatic. In fact, +/// there is few variations between the three channels, so we can say +/// that it is like a grey image. We can with no too distortions +/// replace the color image by the grey one. The improving is in first +/// creating a map of the difference. As we can't keep the free +/// differences between channels, we look at reducing the test and we +/// find an equivalent one based on the difference between minima +/// channel value and the maxima channel value. After the map is +/// ready, we make binarization with the threshold. Then we compute +/// the histogram 1d for every pixels of the map that are greater the +/// threshold. Then, we count pixels in the histogram and traduce the +/// count in percentage to compare to the second threshold. Details +/// are saved in files and printed in the screen. +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; +} + + +/// \brief The main entry. +/// +/// Deal with boost library for walking over image database +/// directories. Call the image processing routine and compute some +/// stats on the result. +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_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/scribo/sandbox/green/exp/annotating/achromastism/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/achromastism/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/achromastism/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/bench/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/bench/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/bench/bench.cc b/scribo/sandbox/green/exp/annotating/bench/bench.cc new file mode 100644 index 0000000..102a896 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/bench/bench.cc @@ -0,0 +1,1450 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// This file is the final work to differentiate between the two image +/// databases AFP and ICDAR. Several tests are used to show +/// improvement of detection. The jonathan's tests are put together to +/// compare them to the others (in fact, to the low saturation one). + +#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> + +/// Histogram part. +/// +/// We regroup here the routines that work on histogram, count_histo, +/// sum_frequency_histo, peak_histo, count_null_frequency_histo, +/// cmp_equi_frequency_histo and others (variance +/// operators). count_histo and sum_frequency_histo count the overall +/// frequency in the domain. peak_histo find the peak of the +/// histogram. count_null_frequency_histo compute the number of non +/// grey levels. cmp_equi_frequency_histo compare a histogram with +/// the one with the same frequency everywhere. +/// \{ +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; +} + +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; +} + +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; +} + +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); +} + +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; +} +/// \} + + +/// \brief Millet Hue detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold a way to adapt the test to the population. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// 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; +} + + +/// \brief Saturation detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold a way to adapt the test to the population. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// 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; +} + + +/// \brief Counting grey levels descriptor. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the number of grey levels. +/// +/// This test aims at compute the number of grey levels. Photographies tends to +/// use all the levels or many of them. +float lvl0_descriptor(mln::image1d<unsigned> histo) +{ + float result; + + // FIXME 255 + result = 255-count_null_frequency_histo(histo); + + return result; +} + + +/// \brief Density hue detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +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; +} + + +/// \brief Density saturation detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +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; +} + + +/// \brief Density value detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +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; +} + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// \param[in] peak the peak of the histogram. +/// \param[in] limit the threshold to compute the contribution. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +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; +} + + +/// \brief Millet value descriptor (cf. Millet.phd.2008) +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold the limit where to compute the contribution. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// 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. +float val1_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; +} + + +/// \brief Error detector (jonathan idea). +/// +/// \param[in] r_img_map the original red channel. +/// \param[in] g_img_map the original green channel. +/// \param[in] b_img_map the original blue channel. +/// \param[in] r_rdc_map the reduced (in color) red channel. +/// \param[in] g_rdc_map the reduced (in color) green channel. +/// \param[in] b_rdc_map the reduced (in color) blue channel. +/// +/// \return the PNSNR (cf compression p278 Handbook Color). +/// +/// \fixme the actual PNSNR is unbound, we need to fix its max to +/// 255. Two images which are the same produces an infinite PNSNR. +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); + +// FIXME: +// SAME IMAGE PRODUCE PNSNR GOING THROW THE INFINITY. +// DIFFERENT IMAGE PRODUCE PNSNR GOING NEAR ZERO. +// WE SHOULD KEEP THE PNSNR BELOW 255 FOR COMPARAISON. + + return error; +} + +/// Discriminant operators. +/// +/// We have some operators that compute the threshold which separate +/// two sub-populations in a histogram. +/// /{ + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (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; +} + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*var1+m2*var2)/(sqrt(v1)+sqrt(v2)). + +float threshold3_histo(float avg1, float var1, float avg2, float var2) +{ + float threshold = (avg1*var1+avg2*var2)/(var1+var2); + + return threshold; +} + + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Gaussian discriminant analysis in 1d is done. Compute the +/// discrimanation and solve the parabolic equation. +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; +} + +/// \brief Minimisation of the error. +/// +/// \param[in] histo_grp1 the histogram of the first population. +/// \param[in] histo_grp2 the histogram of the second population. +/// +/// \return the threshold. +/// +/// Computes the error and find the minimum error threshold. It's just +/// a counting of element in four categories (0 - but detects 1, 1 - +/// but detects 1, 1 but detects 0, 0 but detects 0). The error is the +/// sum of missclassifications for the classes 0 and 1. As Otsu done, +/// we can iterate on the threshold and compute the error associated +/// to it. The bes threshold is the one that minimize the error. +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; +} + + +/// \brief Fisher analysis. +/// +/// \param[in] histo the histogram of the mixed population. +/// +/// \return the threshold. +/// +/// This routine is less performant than the others because it starts +/// with a mixed population, but we know exactly the two separated +/// population. It was just a test. Don't take it in production +/// environement, it doesn't help you. +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; +} +/// /} + + +/// Launching part. +/// +/// In this part, we have the front end that walk over the image +/// databases and start to compute information that we pass to +/// previous routines. +/// \{ + +#define LVL0_DESCR 0 // Number of grey available +#define HUE0_DESCR 1 // Density histogram hue analysis +#define HUE1_DESCR 2 // Millet Hue analysis +#define SAT0_DESCR 3 // Density histogram saturation analysis +#define SAT1_DESCR 4 // Millet saturation analysis +#define VAL0_DESCR 5 // Density histogram grey level analysis +#define VAL1_DESCR 6 // Millet grey level analysis +#define GMP0_DESCR 7 // PNSNR with GIMP compression +#define GMP1_DESCR 8 // PNSNR with GIMP compression +#define GMP2_DESCR 9 // PNSNR with GIMP compression +#define MGK0_DESCR 9 // PNSNR with ImageMagick compression +#define MGK1_DESCR 10 // PNSNR with ImageMagick compression +#define MGK2_DESCR 11 // PNSNR with ImageMagick compression + +#define MGK_DESCR(version) (MGK0_DESCR + version) +#define GMP_DESCR(version) (GMP0_DESCR + version) + +#define NB_DESCR 12 // Number of descriptors +#define NB_DATABASE 2 // Number of image databases : AFP,ICDAR +#define NB_IMAGE 110 // Number of images +#define NB_VERSION 3 // Number of compression by GIMP or ImageMagick + +/// \brief Make sure that the result is between 0 and 255. +/// +/// \param[in] file_name the name of the analysis files. +/// \param[in] result the values for each image and each descriptors. +/// \param[in] size the number of image by each database. +/// +/// Correct the descriptors if there is overflow values. Just used in +/// debug mode. +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; +} + + +/// \brief Dump gnuplot file for vizualisation of results. +/// +/// \param[in] file_name the name of the analysis files. +/// \param[in] result the values for each image and each descriptors. +/// \param[in] size the number of image by each database. +/// +/// This routine aimed at plotting the result with one graph. Each +/// database has got its color. We can see the values used by each +/// descriptors along the image of each database. +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; + } +} + + +/// \brief Compute the descriptor histograms for each database. +/// +/// \param[in] result the result values of each descriptors. +/// \param[in] size the number of images by database. +/// \param[out] histo the computed histograms. +/// +/// This routine compute a histograms for each database and each descriptors. +/// So we can see the distribution of descriptor values over each database. +/// Result values are transform from float to int for histograms. +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][VAL0_DESCR]+0.4999); + mln::opt::at(histo[VAL0_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][VAL1_DESCR]+0.4999); + mln::opt::at(histo[VAL1_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++; + } + } +} + + +/// Compute the threshold for discrimination between ICDAR and AFP. +/// +/// \param[in] histo the histogram for each descriptor and for each database. +/// \param[out] threshold computed values to discriminate between ICDAR/AFP. +/// \param[out] c00 say CLASS 1 but CLASS 1. +/// \param[out] c10 say CLASS 2 but CLASS 1. +/// \param[out] c01 say CLASS 1 but CLASS 2. +/// \param[out] c11 say CLASS 2 but CLASS 2. +/// +/// Compute the threshold by the minimum of error of +/// classification. Population are inverted in function of average, +/// technical solution to prevent min_error crashed. The bad thing is +/// now, the class 0 can be either ICDAR of AFP, depends on descriptors. +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; + + } +} + + +/// \brief Walk over the database directories. +/// +/// \param[out] file_name the array of the image name. +/// \param[out] result the descriptors values for each image. +/// \param[out] size the number of image for each database. +/// +/// This is the front end part to access to the database directories. We use +/// boost library to do it. First we explore ICDAR database en then the AFP one. +/// For each valid image, we compute all the descriptors. Histograms are +/// computed at this level because we can do that thing one time for every +/// density descriptors. Every parameters are updated to retrurn the values. +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] = val1_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; + } +} + +/// \brief Just for debugging purpose and tests. +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; +} + + +/// \brief Main entry. +/// +/// This is the front end for every routines. +/// - compute descriptors. +/// - dump descriptors. +/// - compute histograms. +/// - compute thresholds. +/// - save results. +/// +/// Global data used: +/// - file_name : all the initial images name, mixed every image database. +/// - result : all the result values for every descriptors and for every images. +/// - histo : the histograms ny database and by descriptors. +/// - size : the number of image by database. +/// - threshold : the computed histogram separation on each descriptors. +/// - cxx : the count of well/bad classified images function of threshold info. +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; +} +/// \} + diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/error/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/error/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/error/error.cc b/scribo/sandbox/green/exp/annotating/error/error.cc new file mode 100644 index 0000000..949b51b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/error/error.cc @@ -0,0 +1,833 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// This file is a prelude just before using the bench source. It puts +/// the light on the Jonathan's idea which wants to compare ICDAR and +/// AFP image database with difference between original image and +/// color reducted one. The key point is to say that when a color +/// image is degraded, the visual quality fall quickly in function of +/// the number of colors left. A contrario, when an image is black and +/// white, this kind of degradation doesn't change the original visual +/// quality. The PNSNR detector is build and learning threshold for +/// automatic classification is done (four algorithms are tested). + +#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/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/stretch.hh> +#include <mln/data/fill.hh> + +#include <mln/fun/v2v/component.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> + +/// Work on histogram. +/// \brief Different routines are presented for computing mean, variance ... +/// \{ + +/// \brief Counting the number of pixels in the histogram. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the number of pixels in the histogram. +/// +/// Such routine is a generic and is generally applied on a portion of +/// the real domain. +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; +} + +/// \brief Summing the values ponderated by their occurences. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the sum ponderated of values by their occurences. +/// +/// This method is not a production routine, it's just a test to debug +/// histogram problem. When we compute something over the histogram, +/// we need to take care of the intermediate type that contains the +/// temporary result. If not the case, the type used is the one of the +/// value of the histogram and we can go to some overflow +/// problems. The trick is to store the value returned by the iterator +/// in some temporary computing type (look at pos in the code). +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; +} + +/// \brief Summing the values ponderated by their occurences. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the sum ponderated of values by their occurences. +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; +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// This computing is safe because the quantity of information stored +/// in sum is low. This formulae doesn't show the overflow problem. +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; +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// This is the debugging code to observe overflow problem. +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); +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// My standard code for the variance. +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); +} +/// \} + + +/// Classify. +/// \brief Classification routines that compute threshold. +/// \{ + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (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; +} + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*var1+m2*var2)/(sqrt(v1)+sqrt(v2)). +float threshold3_histo(float avg1, float var1, float avg2, float var2) +{ + float threshold = (avg1*var1+avg2*var2)/(var1+var2); + + return threshold; +} + + + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Gaussian discriminant analysis in 1d is done. Compute the +/// discrimanation and solve the parabolic equation. +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; +} + +/// \brief Compute the other proportion of histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the ponderated sum of the sqrt values. +/// +/// This is a test. Don't take this routine in production. +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; +} + +/// \brief Minimisation of the error. +/// +/// \param[in] histo_grp1 the histogram of the first population. +/// \param[in] histo_grp2 the histogram of the second population. +/// +/// \return the threshold. +/// +/// Computes the error and find the minimum error threshold. It's just +/// a counting of element in four categories (0 - but detects 1, 1 - +/// but detects 1, 1 but detects 0, 0 but detects 0). The error is the +/// sum of missclassifications for the classes 0 and 1. As Otsu done, +/// we can iterate on the threshold and compute the error associated +/// to it. The bes threshold is the one that minimize the error. +short min_error(const mln::image1d<float> histo_grp1, + const mln::image1d<float> histo_grp2) +{ + 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; + } + } + + return threshold; +} + +/// \brief Fisher analysis. +/// +/// \param[in] histo the histogram of the mixed population. +/// +/// \return the threshold. +/// +/// This routine is less performant than the others because it starts +/// with a mixed population, but we know exactly the two separated +/// population. It was just a test. Don't take it in production +/// environement, it doesn't help you. +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; +} +/// \} + +/// \brief ERROR (MSE, PNSNR) compression p278 Handbook Color. +/// \param[in] original the original image. +/// \param[in] reduced the image with a reduced number of colors. +/// +/// \return the PNSNR. +/// +/// Compute the PNSNR. First compute the square difference in each +/// channel. Then compute the mean of those square differences and get +/// the mean over the three channels. Then, get the sqrt of that to +/// have a distance. The PNSNR is a logarithmic quantity of that distance. +float error_test(const std::string original, + const std::string reduced) + +{ + + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::fun::v2v::component<t_rgb8,0> t_component_red; + typedef mln::fun::v2v::component<t_rgb8,1> t_component_green; + typedef mln::fun::v2v::component<t_rgb8,2> t_component_blue; + typedef mln::accu::meta::stat::mean t_mean; + typedef mln::accu::meta::stat::histo1d t_histo; + typedef mln::image2d<t_int_u8> t_img; + typedef mln_trait_op_minus_(t_img,t_img) t_minus; + typedef mln_trait_op_times_(t_minus,t_minus) t_times; + + mln::image2d<mln::value::rgb8> original_rgb8; + mln::image2d<mln::value::rgb8> reduced_rgb8; + + mln::image2d<mln::value::int_u8> original_red; + mln::image2d<mln::value::int_u8> original_green; + mln::image2d<mln::value::int_u8> original_blue; + + mln::image2d<mln::value::int_u8> reduced_red; + mln::image2d<mln::value::int_u8> reduced_green; + mln::image2d<mln::value::int_u8> reduced_blue; + +// mln::image2d<mln::value::int_u8> map_red; +// mln::image2d<mln::value::int_u8> map_green; +// mln::image2d<mln::value::int_u8> map_blue; + +// mln::image1d<unsigned> histo_red; +// mln::image1d<unsigned> histo_green; +// mln::image1d<unsigned> histo_blue; + + 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; + + + mln::io::ppm::load(original_rgb8, original.c_str()); + mln::io::ppm::load(reduced_rgb8, reduced.c_str()); + + original_red = mln::data::transform(original_rgb8, t_component_red()); + original_green = mln::data::transform(original_rgb8, t_component_green()); + original_blue = mln::data::transform(original_rgb8, t_component_blue()); + + reduced_red = mln::data::transform(reduced_rgb8, t_component_red()); + reduced_green = mln::data::transform(reduced_rgb8, t_component_green()); + reduced_blue = mln::data::transform(reduced_rgb8, t_component_blue()); + + minus_red = (original_red - reduced_red); + times_red = minus_red * minus_red; + + minus_green = (original_green - reduced_green); + times_green = minus_green * minus_green; + + minus_blue = (original_blue - reduced_blue); + 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); + +// map_red = mln::data::stretch(t_int_u8(), times_red); +// map_green = mln::data::stretch(t_int_u8(), times_blue); +// map_blue = mln::data::stretch(t_int_u8(), times_green); + +// histo_red = mln::data::compute(t_histo(), map_red); +// histo_green = mln::data::compute(t_histo(), map_green); +// histo_blue = mln::data::compute(t_histo(), map_blue); + +// mln::io::plot::save_image_sh(histo_red, "histo_red.sh"); +// mln::io::plot::save_image_sh(histo_green,"histo_green.sh"); +// mln::io::plot::save_image_sh(histo_blue, "histo_blue.sh"); + +// mln::io::pgm::save(map_red, "red.pgm"); +// mln::io::pgm::save(map_green,"green.pgm"); +// mln::io::pgm::save(map_blue, "blue.pgm"); + + 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 + + return error; +} + + +/// \brief The main entry. +/// +/// This is the front end for error classification. This is the big +/// loop where we walk other image data bases. Computation of +/// automatic thresholds are called from here. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + mln::image1d<float> histo(256); + mln::image1d<float> histo_grp[2]; // histo by group + + histo_grp[0].init_(mln::box1d(mln::point1d(0),mln::point1d(255))); + histo_grp[1].init_(mln::box1d(mln::point1d(0),mln::point1d(255))); + + mln::data::fill(histo, mln::literal::zero); + mln::data::fill(histo_grp[0], mln::literal::zero); + mln::data::fill(histo_grp[1], mln::literal::zero); + + t_path original_path[] = {ICDAR_20P_INPUT_IMG_PATH, + AFP_PPM_IMG_PATH}; + +// t_path reduced1_path[] = {ICDAR_20P_MGK30_IMG_PATH, +// AFP_MGK30_IMG_PATH}; + +// t_path reduced1_path[] = {ICDAR_20P_MGK20_IMG_PATH, +// AFP_MGK20_IMG_PATH}; + + t_path reduced1_path[] = {ICDAR_20P_MGK10_IMG_PATH, + AFP_MGK10_IMG_PATH}; + +// t_path reduced2_path[] = {ICDAR_20P_GMP30_IMG_PATH, +// AFP_GMP30_IMG_PATH}; + +// t_path reduced2_path[] = {ICDAR_20P_GMP20_IMG_PATH, +// AFP_GMP20_IMG_PATH}; + + t_path reduced2_path[] = {ICDAR_20P_GMP10_IMG_PATH, + AFP_GMP10_IMG_PATH}; + + + std::cout << "#!/usr/bin/gnuplot" << std::endl; + std::cout << "set terminal x11 persist 1" << std::endl; + std::cout << "ERROR" << std::endl; + std::cout << "plot '-' using 1 with point notitle,\\" << std::endl; + std::cout << " '-' using 1 with point notitle" << std::endl; + + for (int i = 0; i < 2; i++) + { + if (boost::filesystem::exists(original_path[i]) && + boost::filesystem::exists(reduced1_path[i]) && + boost::filesystem::exists(reduced2_path[i]) && + boost::filesystem::is_directory(original_path[i]) && + boost::filesystem::is_directory(reduced1_path[i]) && + boost::filesystem::is_directory(reduced2_path[i])) + { + boost::filesystem::system_complete(original_path[i]); + const t_iter_path end_iter; + float error1 = 0.0; + float error2 = 0.0; + t_path leaf; + t_path reduced1_file; + t_path reduced2_file; + + for (t_iter_path dir_iter(original_path[i]);end_iter!=dir_iter;++dir_iter) + { + leaf = dir_iter->path().leaf(); + reduced1_file = reduced1_path[i] / leaf; + reduced2_file = reduced2_path[i] / leaf; + + error1 = error_test(dir_iter->path().string(), reduced1_file.string()); + error2 = error_test(dir_iter->path().string(), reduced2_file.string()); + + float a1 = 1; + short v1 = (short)mln::math::floor(error2+0.4999); + mln::opt::at(histo,v1) += a1; + mln::opt::at(histo_grp[i],v1) += a1; + +// float a1 = error2 - mln::math::floor(error2); +// float a2 = mln::math::ceil(error2) - error2; +// short v1 = (short)mln::math::floor(error2); +// short v2 = (short)mln::math::ceil(error2); +// mln::opt::at(histo,v1) += a1; +// mln::opt::at(histo,v2) += a2; +// mln::opt::at(histo_grp[i],v1) += a1; +// mln::opt::at(histo_grp[i],v2) += a2; + + std::cout << error1 << " "; + std::cout << error2 << " "; + std::cout << "# " << dir_iter->path().leaf() << std::endl; + } + std::cout << "e" << std::endl; + } + } + + mln::io::plot::save_image_sh(histo, "histo.sh"); + mln::io::plot::save_image_sh(histo_grp[1], "histo_grp1.sh"); + mln::io::plot::save_image_sh(histo_grp[0], "histo_grp2.sh"); + + float threshold = fisher_analysis(histo); + float threshold2 = threshold_histo(avg_histo(histo_grp[1]), + var_histo(histo_grp[1]), + avg_histo(histo_grp[0]), + var_histo(histo_grp[0])); + float threshold3 = threshold2_histo(avg_histo(histo_grp[1]), + var_histo(histo_grp[1]), + avg_histo(histo_grp[0]), + var_histo(histo_grp[0])); + float threshold4 = min_error(histo_grp[1],histo_grp[0]); + + std::cout << "threshold = " << threshold << std::endl; + std::cout << "threshold2 = " << threshold2 << std::endl; + std::cout << "threshold3 = " << threshold3 << std::endl; + std::cout << "threshold4 = " << threshold4 << std::endl; + std::cout << "avg_grp1 = " << avg_histo(histo_grp[1]) << std::endl; + std::cout << "avg_grp2 = " << avg_histo(histo_grp[0]) << std::endl; + + // compute the classification matrix + // for each sub population + + float c00 = cnt_histo(histo_grp[1] | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + + float c10 = cnt_histo(histo_grp[1] | mln::box1d(mln::point1d(threshold+1), + mln::point1d(255))); + + float c01 = cnt_histo(histo_grp[0] | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + + float c11 = cnt_histo(histo_grp[0] | mln::box1d(mln::point1d(threshold+1), + mln::point1d(255))); + + + std::cout << "pop0 = " << cnt_histo(histo_grp[1]) << std::endl; + std::cout << "pop1 = " << cnt_histo(histo_grp[0]) << std::endl; + std::cout << std::endl; + + std::cout << "c00 = " << c00 << std::endl; + std::cout << "c10 = " << c10 << std::endl; + std::cout << "c01 = " << c01 << std::endl; + std::cout << "c11 = " << c11 << std::endl; + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/histo/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/histo/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/histo/histo.cc b/scribo/sandbox/green/exp/annotating/histo/histo.cc new file mode 100644 index 0000000..90b18e4 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/histo/histo.cc @@ -0,0 +1,366 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Build normalized histograms for visualization only. +/// +/// The goal of this code is to build every normalized histograms of +/// an image database for visual inspection. It enables the +/// comparaison of the densities of each image database and the +/// understanding of the differences in this space. + + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.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/data/convert.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/zero.hh> +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> +#include <mln/math/sqr.hh> +#include <mln/math/sqrt.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.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/fun/v2v/component.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +//============================================================================// +// HISTOGRAM +//============================================================================// + +/// \brief Count the number of pixel in the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the number of pixels in the domain. +/// +/// This is simple counting routine that compute the number of pixels +/// in the domain of the histogram. For each site, it sums the +/// frequencies associated to its site. +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) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + cnt += histo(p); + } + + return cnt; +} + +/// \brief The computing of the normalized histogram. +/// +/// \param[in] input the name of the input image. +/// \param[in] output_map the name of the specific map (hue,sat,val,R,G,B). +/// \param[in] output_histo the name of the output normalized histogram. +/// \param[in] space the name of the studied channel (R,G,B,H,S,V). +/// +/// Compute the normalized histogram in the given channel. First get +/// the channel from the RGB space or the HSV space and then compute +/// its normalized histogram. Normalization is done by dividing each +/// frequency by the sum of frequencies. It's a way to make the +/// histogram independant from the dimension of the input image and +/// then to compare different image databases together. +void histo(const std::string input, + const std::string output_map, + const std::string output_histo, + const char space) + +{ + typedef mln::value::rgb8 t_rgb8; + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_map; + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_sat_map; + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_val_map; + typedef mln::fun::v2v::component<t_rgb8,0> t_component_r; + typedef mln::fun::v2v::component<t_rgb8,1> t_component_g; + typedef mln::fun::v2v::component<t_rgb8,2> t_component_b; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float sum; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + switch(space) + { + case 'h': map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); break; + case 's': map = mln::data::transform(input_rgb8, t_rgb_to_sat_map()); break; + case 'v': map = mln::data::transform(input_rgb8, t_rgb_to_val_map()); break; + case 'r': map = mln::data::transform(input_rgb8, t_component_r()); break; + case 'g': map = mln::data::transform(input_rgb8, t_component_g()); break; + case 'b': map = mln::data::transform(input_rgb8, t_component_b()); break; + default: break;// crash + } + + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = cnt_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + + mln::io::pgm::save(map, output_map.c_str()); + mln::io::plot::save_image_sh(histo_float, output_histo.c_str()); +} + +//============================================================================// +// MAIN +//============================================================================// + + +/// \brief The main entry. +/// +/// This a front end to compute histogram with boost plumberies. There +/// is 2 uses cases, the computing over the icdar database and over +/// the afp database. We force the computing over the six channel +/// (H,S,V,R,G,B) in order to select the channel the most specific for +/// a database. It seems that saturation channel and value channel are +/// the most specific to describe each database. Take care to +/// coherence of the output directories for each source of images. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + +// t_path full_path[] = {t_path(ICDAR_20P_TEXT_ONLY_IMG_PATH), +// t_path(ICDAR_20P_TEXT_COLOR_IMG_PATH), +// t_path(ICDAR_20P_TEXT_PHOTO_IMG_PATH)}; + +// t_path full_path[] = {t_path(AFP_INPUT_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP30_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP20_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP10_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK30_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK20_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK10_IMG_PATH)}; + +// t_path full_path[] = {t_path(ICDAR_20P_INPUT_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_GMP30_IMG_PATH)}; + t_path full_path[] = {t_path(ICDAR_20P_GMP20_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_GMP10_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK30_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK20_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK10_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + 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; + t_path directory; + t_path leaf; + t_path output_map; + t_path output_histo; + + std::cerr << "entering " << full_path[i] << std::endl; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + std::cerr << dir_iter->path() << std::endl; + + leaf = dir_iter->path().leaf(); + +// directory = ANNOTATING_AFP_R_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_R_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_R_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_R_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_R_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'r'); + +// directory = ANNOTATING_AFP_G_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_G_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_G_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_G_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_G_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'g'); + +// directory = ANNOTATING_AFP_B_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_B_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_B_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_B_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_B_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'b'); + +// directory = ANNOTATING_AFP_H_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_H_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_H_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_H_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_H_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'h'); + +// directory = ANNOTATING_AFP_S_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_S_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_S_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_S_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_S_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 's'); + +// directory = ANNOTATING_AFP_V_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_V_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_V_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_V_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_V_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'v'); + } + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/hsv/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/hsv/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/hsv/hsv.cc b/scribo/sandbox/green/exp/annotating/hsv/hsv.cc new file mode 100644 index 0000000..507d07b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/hsv/hsv.cc @@ -0,0 +1,912 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet HSV operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Hue +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +/// +/// This operator try to integrate many processing in one shot, after +/// HSV transformation. + + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.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/data/convert.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/zero.hh> +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> +#include <mln/math/sqr.hh> +#include <mln/math/sqrt.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.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/value/rgb8.hh> + +//============================================================================// +// HISTOGRAM +//============================================================================// + +/// Histogram +/// \{ +/// \brief Histogram processing methods. +/// +/// In this part, we define methods for histogram processing as average, +/// peak, variance etc ... + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + +/// \brief Sum the whole frequencies. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the number of pixels in the domain. +/// +/// By summing all the frequencies of the domain, we count in fact the +/// number of pixels in the image. +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; +} + +/// \brief Count the null frequencies. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the number of null frequencies. +/// +/// It's a way to access the number of distinct colors in the +/// histogram by comparing it with the histogram domain. +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; +} + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +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; +} + +/// \brief Find the maximum frequency of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the maximum frequency of the histogram. +/// +/// Find the peak and return its value, not its position. We use that +/// function to normalize histograms. +template <typename I> +mln_value(I) max_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the value of the peak from the histogram + mln_value(I) max = mln::opt::at(histo, mln::literal::zero); + mln_piter(I) p(histo.domain()); + + for_all(p) + { + max = mln::math::max(histo(p),max); + } + + return max; +} + +/// \brief Find the mean of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the aveerage of the histogram. +/// +/// Compute the mean. +template <typename I> +float mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief Compare the histogram with the equi distributed histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the distance between the equi distributed histogram. +/// +/// Compute the square euclidian distance between histogram and the +/// equi distributed histogram. The equi distributed histogram is an +/// histogram in which each bin has the same frequency (id est 1/256 +/// for instance). The integral equals one for this two +/// histograms. This is a major contribution test to differentiate +/// image database. +template <typename I> +float cmp_equi_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + var += mln::math::sqr(histo(p) - (1/256.0)); + } + + var = var / sum; + + return var; +} + +/// \brief Compute the variance on the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the variance. +/// +/// Compute the variance by substracting the mean. +template <typename I> +float var_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + var += mln::math::sqr(p.ind() - mean) * histo(p); + } + + var = var / sum; + + return var; +} + +/// \brief Find the mean of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the aveerage of the histogram. +/// +/// Compute the mean. +template <typename I> +float mean_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum++; + mean += histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief Compute the standard deviation on the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the dstandard deviation. +/// +/// Compute the standard deviation by substracting the mean +template <typename I> +float stddev_frequency_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the var of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum++; + var += mln::math::sqr(histo(p)-mean); + } + + var = mln::math::sqrt(var / sum); + + return var; +} +/// \} + +//============================================================================// +// HUE TEST +//============================================================================// + +/// Hue test +/// \{ +/// \brief Hue test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the hue analysis. + + +/// \brief Label a grey value. +/// +/// \param[in] int_u8 the grey value. +/// +/// \return the reference color for this grey value. +/// +/// Classify grey value in three class, white, black and medium_gray. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the +/// primary colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + + if (10 > hue) + result = mln::literal::red; + else if (32 > hue) + result = mln::literal::orange; + else if (53 > hue) + result = mln::literal::yellow; + else if (74 > hue) + result = mln::literal::green; // chartreuse + else if (96 > hue) + result = mln::literal::green; + else if (116 > hue) + result = mln::literal::green;// turquoise, aigue-marine + else if (138 > hue) + result = mln::literal::green; // cyan + else if (159 > hue) + result = mln::literal::blue; // azur + else if (181 > hue) + result = mln::literal::blue; + else if (202 > hue) + result = mln::literal::violet; + else if (223 > hue) + result = mln::literal::pink; + else // if (244 > hue) + result = mln::literal::red; + + return result; +} + +/// \brief The hue test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// \param[in] threshold the limit to compute the histogram contribution. +/// +/// \return the proportion of pixels that pass the test. +/// +/// This is an augmented test. We first normalized the histogram to +/// obtain a discrete distribution. Then we compute the mean (or the +/// peak, i don't really know what is the best) and we look at the +/// proportion of the pixels around the mean or the peak. This +/// proportion tell us how histogram distribution is concentrated +/// around the modal value. If a hue modal value exist, we can say +/// that the image is colorized. +float hue_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float prop; + short peak; + mln::value::rgb8 color; + float sum; + mln::point1d inf; + mln::point1d sup; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + peak = mean_histo(histo); //peak_histo(histo); + color = label_hue(peak); + inf = mln::point1d(mln::math::max(0, peak-threshold)); + sup = mln::point1d(mln::math::min(255, peak+threshold)); + cnt1 = count_histo(histo_float|mln::box1d(inf,sup)); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); +// std::cout << "peak = " << peak << std::endl; +// std::cout << "color = " << color << std::endl; + + return prop; +} +/// \} + +//============================================================================// +// SATURATION TEST +//============================================================================// + +/// Saturation test +/// \{ +/// \brief Saturation test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the +/// saturation analysis. + + +/// \brief The saturation test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// \param[in] threshold for deciding which is low saturation or not. +/// +/// \return the proportion of pixels which pass the test. +/// +/// This is the augmented test, we normalized the histogram and then +/// we count the proportion of the histogram below the threshold. +float saturation_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_saturation_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float sum; + float prop; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_saturation_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + cnt1 = count_histo(histo_float | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} +/// \} + +//============================================================================// +// VALUE TEST +//============================================================================// + +/// Value test +/// \{ +/// \brief Value test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the +/// value analysis. + + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +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; +} + + +/// \brief This is the Value test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of result histogram. +/// \param[in] tmp the name of the output value (HSV) map. +/// \param[in] threshold the range around the peak where R is computed. +/// +/// \return the proportion of pixels that pass the test. +/// +/// This is the augmented version of the Millet test. This code is not +/// stable. There is a lots of tests on. The Millet test is computed +/// with stddev2 call. We test simple counting around the pic as we +/// do for other tests but it's not really insteresting. The prop4 is +/// the best for me. We test the difference between the distribution and the +/// equi-distribution. +float value_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float prop; + float sum; + float prop4; + short peak; + mln::point1d inf; + mln::point1d sup; + + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_value_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + prop4 = cmp_equi_frequency_histo(histo_float); + peak = peak_histo(histo); // mean_histo(histo); + //prop = stddev2(histo, peak, threshold); + inf = mln::point1d(mln::math::max(0, peak-threshold)); + sup = mln::point1d(mln::math::min(255, peak+threshold)); + cnt1 = count_histo(histo_float|mln::box1d(inf,sup)); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + std::cerr << "peak = " << peak << std::endl; + std::cerr << "inf = " << inf << std::endl; + std::cerr << "sup = " << sup << std::endl; + std::cerr << "cnt1 = " << cnt1 << std::endl; + std::cerr << "cnt2 = " << cnt2 << std::endl; + std::cerr << "prop = " << prop << std::endl; + std::cerr << "prop4= " << prop4 << std::endl; + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + +//============================================================================// +// MAIN +//============================================================================// + + +/// \brief The main entry. +/// +/// This is a front end for launching image processing test. We deal +/// with boost library to walk on image database directories. Nothing +/// very interesting here, it is a lot of plumberies. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + +// t_path full_path[] = {t_path(ICDAR_20P_TEXT_ONLY_IMG_PATH), +// t_path(ICDAR_20P_TEXT_COLOR_IMG_PATH), +// t_path(ICDAR_20P_TEXT_PHOTO_IMG_PATH)}; + + t_path full_path[] = {t_path(AFP_PPM_IMG_PATH)}; + + std::cout << "#!/usr/bin/gnuplot" << std::endl; + std::cout << "set terminal x11 persist 1" << std::endl; + std::cout << "#HUE - SATURATION - VALUE" << std::endl; + std::cout << "plot '-' using 1:2 with point notitle,\\" << std::endl; + std::cout << " '-' using 1:2 with point notitle,\\" << std::endl; + std::cout << " '-' using 1:2 with point notitle" << std::endl; + + for (int i = 0; i < 1; ++i) + { + 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; + t_path directory; + t_path leaf; + t_path output; + t_path tmp; + + std::cerr << "entering " << full_path[i] << std::endl; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + std::cerr << dir_iter->path() << std::endl; + // concatenation de chaine +// directory = (ANNOTATING_ICDAR_H_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_H_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = hue_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 20); + + std::cout << prop << " "; + +// directory = (ANNOTATING_ICDAR_S_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_S_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = saturation_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 25); + + std::cout << prop << " "; + +// directory = (ANNOTATING_ICDAR_V_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_V_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = value_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 15); + + std::cout << prop << " "; + std::cout << "# " << dir_iter->path().leaf() << std::endl; + } + std::cout << "e" << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/hue/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/hue/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/hue/hue.cc b/scribo/sandbox/green/exp/annotating/hue/hue.cc new file mode 100644 index 0000000..6e1195c --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/hue/hue.cc @@ -0,0 +1,402 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet hue operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Hue +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. + + +#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/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_hue_map.hh> +#include <mln/value/rgb8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + + + +/// \brief Label a grey value. +/// +/// \param[in] int_u8 the grey value. +/// +/// \return the reference color for this grey value. +/// +/// Classify grey value in three class, white, black and medium_gray. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the +/// primary colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + + if (10 > hue) + result = mln::literal::red; + else if (32 > hue) + result = mln::literal::orange; + else if (53 > hue) + result = mln::literal::yellow; + else if (74 > hue) + result = mln::literal::green; // chartreuse + else if (96 > hue) + result = mln::literal::green; + else if (116 > hue) + result = mln::literal::green;// turquoise, aigue-marine + else if (138 > hue) + result = mln::literal::green; // cyan + else if (159 > hue) + result = mln::literal::blue; // azur + else if (181 > hue) + result = mln::literal::blue; + else if (202 > hue) + result = mln::literal::violet; + else if (223 > hue) + result = mln::literal::pink; + else // if (244 > hue) + result = mln::literal::red; + + +// if (14 > hue) +// result = mln::literal::red; +// else if (29 > hue) +// result = mln::literal::orange; +// else if (45 > hue) +// result = mln::literal::yellow; +// else if (113 > hue) +// result = mln::literal::green; +// else if (149 > hue) +// result = mln::literal::cyan; +// else if (205 > hue) +// result = mln::literal::blue; +// else if (235 > hue) +// result = mln::literal::violet; +// else if (242 > hue) +// result = mln::literal::pink; +// else +// result = mln::literal::red; + + return result; +} + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +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; +} + +/// \brief Compute the average of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the average of the histogram. +/// +/// Compute the mean of the histogram by summing the values of each +/// bin ponderated by its frequency. The result is divided by the sum +/// of the frequencies. +template <typename I> +unsigned mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + + +/// \brief The hue test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// +/// \return the proportion of pixels that pass the test. +/// +/// Load the input image, transform it to get the hue_map and compute +/// the histogram. Then count the number of pixels that are between +/// peak-threshold and peak +threshold. Print the peak and the +/// percentage of pixels around the pic. +float hue_test(const std::string input, + const std::string output, + const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_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; + unsigned peak; + mln::value::rgb8 color; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + peak = mean_histo(histo); //peak_histo(histo); + color = label_hue(peak); + cnt1 = count_histo(histo | mln::box1d(mln::point1d(peak-threshold), + mln::point1d(peak+threshold))); + cnt2 = mln::geom::nsites(input_rgb8); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + std::cout << "peak = " << peak << std::endl; + std::cout << "color = " << color << std::endl; + + return prop; +} + + +/// \brief Main entry. +/// +/// It is a front end which managing the iteration through one image +/// data base with boost. Save hue map and hue histogram. +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_ICDAR_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = hue_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 20); + + 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/scribo/sandbox/green/exp/annotating/hue/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/hue/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/hue/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/hue/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/hue/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/hue/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/nb_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/nb_color/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc b/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc new file mode 100644 index 0000000..2331c61 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc @@ -0,0 +1,171 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Compute the number of colors in an image. +/// +/// Build histogram of colors and count the bins different from zero. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +// #include <mln/morpho/opening/volume.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/pw/value.hh> +#include <mln/pw/cst.hh> + +#include <mln/util/timer.hh> + +#include <mln/value/rgb8.hh> +//#include <mln/value/rgb.hh> + + +/// \brief Count the colors. +/// +/// \param[in] image the name of the image to process. +/// +/// \return the number of colors. +/// +/// Count the color by building histogram. Strange filtering is +/// done. We use also the technique of regional maxima in +/// comment. Quantification is done to reduce the size of the +/// histogram as well. + +// n < 8, n is the degree of quantification +template <unsigned n> +unsigned count_image_color(const std::string& image) +{ + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::image3d<bool> t_histo3d_bool; + typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::meta::math::count t_count_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + t_histo3d histo; +// t_histo3d opened; + t_histo3d_bool filtered; + + mln::io::ppm::load(input_rgb8, image.c_str()); + + unsigned nb_pixel = input_rgb8.ncols() * input_rgb8.nrows(); + unsigned min_volume = (unsigned)(nb_pixel * 0.0001); + unsigned nb_color = 0; + +// input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb8); +// opened = mln::morpho::opening::volume(histo, mln::c6(), min_volume); + filtered = mln::binarization::threshold(histo, min_volume); + nb_color = mln::data::compute(t_count_fun(), + (filtered|(mln::pw::value(filtered)!=0)).rw()); + + return nb_color; +} + + +/// \brief Main entry. +/// +/// The main routine makes the base driver job. It looks after many +/// directories and lists every images in it. We use the boost library +/// to do it. Statistics are computed on the number of colors. The +/// first idea was to answer the question is this descriptor allow to +/// recognize a specific base. +int main() +{ + typedef boost::filesystem::path t_path; + //typedef boost::filesystem::initial_path<t_path()> t_init_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + 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; + unsigned count = 0; + unsigned sum1 = 0; + unsigned sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + unsigned val = count_image_color<8>(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/saturation/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/saturation/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/saturation/saturation.cc b/scribo/sandbox/green/exp/annotating/saturation/saturation.cc new file mode 100644 index 0000000..b5834a1 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/saturation/saturation.cc @@ -0,0 +1,175 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet saturation operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Sat +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +#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_saturation_map.hh> + +#include <mln/io/ppm/load.hh> +//#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + +/// \brief The saturation test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] threshold for deciding which is low saturation or not. +/// +/// \return the proportion of pixels that pass the test. +/// +/// Load the input image, transform it to get the saturation_map and compute +/// the histogram. Then count the number of pixels that are under the threshold. +/// Then return the proportion of pixels. +float saturation_test(const std::string input, + const std::string output, +// const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_saturation_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_saturation_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()); +// mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + + +/// \brief the main entry. +/// +/// This is a front end for image processing routine. It manages the +/// calling of every image in the database. +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_ICDAR_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = saturation_test(dir_iter->path().string(), + output.string(), +// tmp.string(), + 25); + + 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/scribo/sandbox/green/exp/annotating/saturation/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/saturation/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/saturation/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/stddev_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/stddev_color/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc b/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc new file mode 100644 index 0000000..365a9c3 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc @@ -0,0 +1,216 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +float stddev_color(const std::string& 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_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + t_histo1d histo; + t_point1d max_site; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + // 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; + + return stddev; +} + + +/// \brief Main entry. +/// +/// Front end for image processing. It is in the main routine where we +/// go through the image data base (using boost to do it). +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + 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 count = 0; + float sum1 = 0; + float sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + float val = stddev_color(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/stddev_color_16/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/stddev_color_16/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc b/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc new file mode 100644 index 0000000..fc55547 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc @@ -0,0 +1,277 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. When photographies +/// has some large uniform border, the detection can fail. To improve +/// the method, Millet decide to subdivise the image in 16 sub images +/// on which it applies the test. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> +#include <mln/data/paste.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> +#include <mln/math/max.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] input_int_u8 the 8 bits input image to analyze. +/// \param[in] name_histo the name of the output histogram. +/// \param[in] name_image the name of the ouput sub image. +/// +/// \return the deviation, but at this time nothing.. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +float stddev_color(mln::image2d<mln::value::int_u8> input_int_u8) +{ + 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; + + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + // 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; + + return stddev; +} + +/// \brief Divide the image in 16 sub images. +/// +/// \param[in] image the input image. +/// +/// \result nothing. +/// +/// Divive the input image in 16 by uniform and geometrical +/// method. When a sub image is ready, call the stddev routine to show +/// stats on it. +float stddev_color_16(const std::string& 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_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 4; + unsigned sz_col = (pmax.col() - pmin.col())/ 4; + float stddev = 0.0; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 4; ++i) + for (unsigned j = 0; j < 4; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + // Save it + t_image2d_int_u8 input_1o16_int_u8(dom); + + mln::data::paste(input_int_u8 | dom, input_1o16_int_u8); + + stddev = mln::math::max(stddev, stddev_color(input_1o16_int_u8)); + } + + return stddev; +} + + +/// \brief Main entry. +/// +/// Just a front end for image processing routine. Use boost to go +/// through the image database. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + 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 count = 0; + float sum1 = 0; + float sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + float val = stddev_color_16(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/value/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/value/Makefile.am diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/scribo/sandbox/green/exp/annotating/value/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/value/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/value/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/value/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/value/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/value/text-only.txt diff --git a/scribo/sandbox/green/exp/annotating/value/value.cc b/scribo/sandbox/green/exp/annotating/value/value.cc new file mode 100644 index 0000000..cade88b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/value/value.cc @@ -0,0 +1,468 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet value operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to value +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +#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/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.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/value/rgb8.hh> + +/// \brief Label the value. +/// +/// \param[in] val the value in [0..255]. +/// +/// \return the reference color. +/// +/// Segment the grey value in three classes. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +/// +/// Discriminate between orange and brown colors which are at the same +/// position on the hue wheel. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +/// +/// Segment between yellow and green because the discrimination is not +/// so easy based on the hue wheel. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the primary +/// colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + if (14 > hue) + result = mln::literal::red; + else if (29 > hue) + result = mln::literal::orange; + else if (45 > hue) + result = mln::literal::yellow; + else if (113 > hue) + result = mln::literal::green; + else if (149 > hue) + result = mln::literal::cyan; + else if (205 > hue) + result = mln::literal::blue; + else if (235 > hue) + result = mln::literal::violet; + else if (242 > hue) + result = mln::literal::pink; + else + result = mln::literal::red; + + return result; +} + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +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; +} + +/// \brief Detect the peak of the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the position of the peak. +/// +/// This is a typical accumulator use case but as it needs the +/// position it couldn't be one of them. Look at the last maxima in +/// the curve and return its position. +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; +} + +/// \brief Compute the average of the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the average of the histogram. +/// +/// This code should be an accumulator. It computes the mean of the +/// histogram, just stats in loop. Compute the sum, compute the +/// ponderate sum and divide the second by the first. +template <typename I> +unsigned mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +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; +} + + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +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; +} + + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// \param[in] peak the peak of the histogram. +/// \param[in] limit the threshold to compute the contribution. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +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; +} + + +/// \brief This is the main Millet test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of result histogram. +/// \param[in] tmp the name of the output value (HSV) map. +/// \param[in] threshold the range around the peak where R is computed. +/// +/// \return the proportion of pixels that pass the test. +float value_test(const std::string input, + const std::string output, + const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_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; + unsigned peak; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_value_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + peak = peak_histo(histo); // mean_histo(histo); + prop = stddev2(histo, peak, threshold); +// cnt1 = count_histo(histo | mln::box1d(mln::point1d(peak-threshold), +// mln::point1d(peak+threshold))); +// cnt2 = mln::geom::nsites(input_rgb8); +// prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + + +/// \brief The main entry. +/// +/// This is the front end for using directories with boost. It calls +/// the true image processing routine. +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_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = value_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 15); + + std::cout << output << " : " << prop << std::endl; + std::cerr << output << " : " << prop << std::endl; + } + } + } + + return 0; +} diff --git a/scribo/sandbox/green/mln/clustering/kmean2d.hh b/scribo/sandbox/green/mln/clustering/kmean2d.hh index 51aaf49..4539083 100644 --- a/scribo/sandbox/green/mln/clustering/kmean2d.hh +++ b/scribo/sandbox/green/mln/clustering/kmean2d.hh @@ -150,7 +150,7 @@ namespace mln { /// Type definitions. /// \brief A few type definitions to limit the refactoring impact. - ///{ + /// \{ typedef value::rgb<8> t_rgb; typedef value::label<8> t_label; typedef value::rg<n> t_value; @@ -179,7 +179,7 @@ namespace mln typedef util::array<t_mean_set> t_mean_cnv; typedef image1d<t_result1d> t_variance_val; typedef util::array<t_variance_val> t_variance_cnv; - ///} + /// \} /// \brief Constructor. /// \param[in] point : the image as the population of pixels. diff --git a/scribo/sandbox/green/mln/fun/p2b/achromatic.hh b/scribo/sandbox/green/mln/fun/p2b/achromatic.hh index 1f45b2d..91a2dc5 100644 --- a/scribo/sandbox/green/mln/fun/p2b/achromatic.hh +++ b/scribo/sandbox/green/mln/fun/p2b/achromatic.hh @@ -44,15 +44,29 @@ namespace mln namespace p2b { - /// \brief Functor that compare the i-th component of a value. - // V is for the type of the value received - // i is the ith component to select + /// \brief Functor that compare the i-th component with a threshold. + /// + /// T_rgb is the kind of RGB we use. template <typename T_rgb> struct achromatic : public Function_v2b< achromatic<T_rgb> > { typedef bool result; + + /// \brief Operator that makes the threshold [millet.phd.2008.pdf] + /// + /// \param[in] p the selected site + /// + /// \return if the site is achromatic or not. + /// + /// This a try for opimized the call to the achromatic + /// routine. The goal is to decide if a site has got an + /// achromatic status or not. This is the true Millet test. bool operator()(const point2d& p) const; + /// \brief Cstor of the object. + /// + /// \param[in] img the RGB input image. + /// \param[in] threshold the value to compare with. achromatic(const image2d<T_rgb>& img, const float threshold); const float threshold_; diff --git a/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh b/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh index 84d26c8..e776c9a 100644 --- a/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh +++ b/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh @@ -32,6 +32,10 @@ # include <mln/value/hsv.hh> # include <mln/value/rgb8.hh> +/// \file +/// +/// This is the code for building hue_concentration_map. + namespace mln { @@ -41,12 +45,18 @@ namespace mln namespace v2v { + /// \brief internal method for detecting the histogram peak. + /// + /// \param[in] hue_histo the histogram of hue. + /// + /// \return the seed of the peek. unsigned peak_histo(const mln::image1d<unsigned>& hue_histo); struct hue_concentration : public Function_v2v< hue_concentration > { typedef float result; + /// \brief Gibe the distance map between actual hue and the peak. float operator()(const float hue) const; hue_concentration(const mln::image1d<unsigned>& hue_histo) diff --git a/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh b/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh index 6de63ec..126f9bd 100644 --- a/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh +++ b/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh @@ -49,6 +49,11 @@ namespace mln /// \brief Convert rgb value to achromatism map. /// + /// Convert rgb value to binary achromastism map using the + /// rebuilded Millet phd formulae [millet.phd.2008.pdf]. The + /// idea is to look at the minimum and maximum of the channels + /// and to return the difference. + /// /// \ingroup modfunv2v template <unsigned n> diff --git a/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh b/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh index 3dd8eb0..a191095 100644 --- a/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh +++ b/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh @@ -34,6 +34,11 @@ #include <mln/value/hsv.hh> #include <mln/value/rgb.hh> +/// \fiie +/// +/// This is the millet [millet.phd.2008.pdf] transformation from RGB +/// space to HSV space. + namespace mln { @@ -57,7 +62,15 @@ namespace mln typedef T_hsv result; - /// HSV implementation from millet.2008.phd.pdf p67 + /// \brief HSV implementation from millet.2008.phd.pdf p67 + /// + /// \param[in] rgb the input rgb pixel. + /// + /// \return a HSV pixel. + /// + /// This is the Millet implementation of its transformation + /// operator to go from RGB space to HSV space. When pixels + /// are achromatic, hue equals -1. template <typename T_rgb> T_hsv operator()(const T_rgb& rgb) const; -- 1.5.6.5
14Â years, 2Â months
1
0
0
0
last-svn-commit-22-ga3cbabf Write the opening volume thresholds for the scribo image mp00082c.ppm.
by Yann Jacquelet
* 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
14Â years, 2Â months
1
0
0
0
last-svn-commit-23-gfdf0f76 Split the regional maxima binary in small atomic binaries.
by Yann Jacquelet
* 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
14Â years, 2Â months
1
0
0
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
* 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
14Â years, 2Â months
1
0
0
0
last-svn-commit-45-g400f1e8 Implement the kmean algorithh and start to optimize it.
by Yann Jacquelet
Implement the first version with vectors and matrices. * mln/clustering/k_mean.hh: New library component. * use/clustering/k_mean: New directory. * use/clustering/k_mean/Makefile.am: New makefile. * use/clustering/k_mean/k_mean.cc: New source file. * tests/clustering/k_mean: New directory. * tests/clustering/k_mean/Makefile.am: New makefile. * tests/clustering/k_mean/k_mean.cc: New source file. Implement the second version with image and working in 1d. * mln/clustering/kmean1d.hh: New library component. * use/clustering/kmean1d: New directory. * use/clustering/kmean1d/Makefile.am: New makefile. * use/clustering/kmean1d/kmean1d.cc: New source file. * demo/clustering/kmean1d: New directory. * demo/clustering/kmean1d/Makefile.am: New makefile. * demo/clustering/kmean1d/kmean1d.cc: New source file. Implement transformation between RG space and RGB space. * mln/fun/v2v/rg_to_rgb.hh: New library component. * use/fun/v2v/rg_to_rgb: New directory. * use/fun/v2v/rg_to_rgb/Makefile.am: New makefile. * use/fun/v2v/rg_to_rgb/rg_to_rgb.cc: New source file. Implement the third version working in 2d (r/g). * mln/clustering/kmean2d.hh: New library component. * use/clustering/kmean2d: New directory. * use/clustering/kmean2d/Makefile.am: New makefile. * use/clustering/kmean2d/kmean2d.cc: New source file. * demo/clustering/kmean2d: New directory. * demo/clustering/kmean2d/Makefile.am: New makefile. * demo/clustering/kmean2d/kmean2d.cc: New source file. Implement the fourth version working in 3d (rgb). * mln/clustering/kmean3d.hh: New library component. * use/clustering/kmean3d: New directory. * use/clustering/kmean3d/Makefile.am: New makefile. * use/clustering/kmean3d/kmean3d.cc: New source file. * demo/clustering/kmean3d: New directory. * demo/clustering/kmean3d/Makefile.am: New makefile. * demo/clustering/kmean3d/kmean3d.cc: New source file. Implement the fith version as a function (working in rgb space). * mln/clustering/kmean_rgb.hh: New library component. * use/clustering/kmean_rgb: New directory. * use/clustering/kmean_rgb/Makefile.am: New makefile. * use/clustering/kmean_rgb/kmean_rgb.cc: New source file. * demo/clustering/kmean_rgb: New directory. * demo/clustering/kmean_rgb/Makefile.am: New makefile. * demo/clustering/kmean_rgb/kmean_rgb.cc: New source file. Benchmark distance algorithm for the kmean algorithm. * bench/clustering/distance: New directory. * bench/clustering/distance/Makefile.am: New makefile. * bench/clustering/distance/distance.cc: New source file. --- scribo/sandbox/green/ChangeLog | 77 ++ scribo/sandbox/green/README.green | 150 ++++- .../green/bench/clustering/distance/Makefile.am | 153 ++++ .../green/bench/clustering/distance/distance.cc | 842 ++++++++++++++++++++ .../green/demo/clustering/kmean1d/Makefile.am | 153 ++++ .../green/demo/clustering/kmean1d/kmean1d.cc | 258 ++++++ .../green/demo/clustering/kmean2d/Makefile.am | 153 ++++ .../green/demo/clustering/kmean2d/kmean2d.cc | 278 +++++++ .../green/demo/clustering/kmean3d/Makefile.am | 153 ++++ .../green/demo/clustering/kmean3d/kmean3d.cc | 265 ++++++ .../green/demo/clustering/kmean_rgb/Makefile.am | 153 ++++ .../green/demo/clustering/kmean_rgb/kmean_rgb.cc | 239 ++++++ scribo/sandbox/green/mln/clustering/k_mean.hh | 365 ++++++--- scribo/sandbox/green/mln/clustering/kmean1d.hh | 194 ++--- scribo/sandbox/green/mln/clustering/kmean2d.hh | 329 ++++---- scribo/sandbox/green/mln/clustering/kmean3d.hh | 246 +++--- scribo/sandbox/green/mln/clustering/kmean_rgb.hh | 87 ++- scribo/sandbox/green/mln/fun/v2v/rg_to_rgb.hh | 59 ++- .../green/tests/clustering/k_mean/Makefile.am | 13 +- .../green/tests/clustering/k_mean/k_mean.cc | 395 ++++++---- .../stat/histo1d => clustering/k_mean}/Makefile.am | 0 .../sandbox/green/use/clustering/k_mean/k_mean.cc | 55 ++ .../histo1d => clustering/kmean1d}/Makefile.am | 0 .../green/use/clustering/kmean1d/kmean1d.cc | 50 ++ .../histo1d => clustering/kmean2d}/Makefile.am | 0 .../green/use/clustering/kmean2d/kmean2d.cc | 61 ++ .../histo1d => clustering/kmean3d}/Makefile.am | 0 .../green/use/clustering/kmean3d/kmean3d.cc | 63 ++ .../histo1d => clustering/kmean_rgb}/Makefile.am | 0 .../green/use/clustering/kmean_rgb/kmean_rgb.cc | 63 ++ .../stat/histo1d => fun/v2v/rg_to_rgb}/Makefile.am | 0 .../green/use/fun/v2v/rg_to_rgb/rg_to_rgb.cc | 68 ++ 32 files changed, 4224 insertions(+), 698 deletions(-) create mode 100644 scribo/sandbox/green/bench/clustering/distance/Makefile.am create mode 100644 scribo/sandbox/green/bench/clustering/distance/distance.cc create mode 100644 scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am create mode 100644 scribo/sandbox/green/demo/clustering/kmean1d/kmean1d.cc create mode 100644 scribo/sandbox/green/demo/clustering/kmean2d/Makefile.am create mode 100644 scribo/sandbox/green/demo/clustering/kmean2d/kmean2d.cc create mode 100644 scribo/sandbox/green/demo/clustering/kmean3d/Makefile.am create mode 100644 scribo/sandbox/green/demo/clustering/kmean3d/kmean3d.cc create mode 100644 scribo/sandbox/green/demo/clustering/kmean_rgb/Makefile.am create mode 100644 scribo/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => clustering/k_mean}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/clustering/k_mean/k_mean.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => clustering/kmean1d}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/clustering/kmean1d/kmean1d.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => clustering/kmean2d}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/clustering/kmean2d/kmean2d.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => clustering/kmean3d}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/clustering/kmean3d/kmean3d.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => clustering/kmean_rgb}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/clustering/kmean_rgb/kmean_rgb.cc copy scribo/sandbox/green/use/{accu/stat/histo1d => fun/v2v/rg_to_rgb}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/use/fun/v2v/rg_to_rgb/rg_to_rgb.cc diff --git a/scribo/sandbox/green/ChangeLog b/scribo/sandbox/green/ChangeLog index 7bb6646..4ddaa54 100644 --- a/scribo/sandbox/green/ChangeLog +++ b/scribo/sandbox/green/ChangeLog @@ -1,3 +1,80 @@ +2010-06-28 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Implement the kmean algorithh and start to optimize it. + + Implement the first version with vectors and matrices. + + * mln/clustering/k_mean.hh: New library component. + + * use/clustering/k_mean: New directory. + * use/clustering/k_mean/Makefile.am: New makefile. + * use/clustering/k_mean/k_mean.cc: New source file. + + * tests/clustering/k_mean: New directory. + * tests/clustering/k_mean/Makefile.am: New makefile. + * tests/clustering/k_mean/k_mean.cc: New source file. + + Implement the second version with image and working in 1d. + + * mln/clustering/kmean1d.hh: New library component. + + * use/clustering/kmean1d: New directory. + * use/clustering/kmean1d/Makefile.am: New makefile. + * use/clustering/kmean1d/kmean1d.cc: New source file. + + * demo/clustering/kmean1d: New directory. + * demo/clustering/kmean1d/Makefile.am: New makefile. + * demo/clustering/kmean1d/kmean1d.cc: New source file. + + Implement transformation between RG space and RGB space. + + * mln/fun/v2v/rg_to_rgb.hh: New library component. + * use/fun/v2v/rg_to_rgb: New directory. + * use/fun/v2v/rg_to_rgb/Makefile.am: New makefile. + * use/fun/v2v/rg_to_rgb/rg_to_rgb.cc: New source file. + + Implement the third version working in 2d (r/g). + + * mln/clustering/kmean2d.hh: New library component. + + * use/clustering/kmean2d: New directory. + * use/clustering/kmean2d/Makefile.am: New makefile. + * use/clustering/kmean2d/kmean2d.cc: New source file. + + * demo/clustering/kmean2d: New directory. + * demo/clustering/kmean2d/Makefile.am: New makefile. + * demo/clustering/kmean2d/kmean2d.cc: New source file. + + Implement the fourth version working in 3d (rgb). + + * mln/clustering/kmean3d.hh: New library component. + + * use/clustering/kmean3d: New directory. + * use/clustering/kmean3d/Makefile.am: New makefile. + * use/clustering/kmean3d/kmean3d.cc: New source file. + + * demo/clustering/kmean3d: New directory. + * demo/clustering/kmean3d/Makefile.am: New makefile. + * demo/clustering/kmean3d/kmean3d.cc: New source file. + + Implement the fith version as a function (working in rgb space). + + * mln/clustering/kmean_rgb.hh: New library component. + + * use/clustering/kmean_rgb: New directory. + * use/clustering/kmean_rgb/Makefile.am: New makefile. + * use/clustering/kmean_rgb/kmean_rgb.cc: New source file. + + * demo/clustering/kmean_rgb: New directory. + * demo/clustering/kmean_rgb/Makefile.am: New makefile. + * demo/clustering/kmean_rgb/kmean_rgb.cc: New source file. + + Benchmark distance algorithm for the kmean algorithm. + + * bench/clustering/distance: New directory. + * bench/clustering/distance/Makefile.am: New makefile. + * bench/clustering/distance/distance.cc: New source file. + 2010-06-24 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Define documentation files. diff --git a/scribo/sandbox/green/README.green b/scribo/sandbox/green/README.green index 3f3c9a9..02d8654 100644 --- a/scribo/sandbox/green/README.green +++ b/scribo/sandbox/green/README.green @@ -33,8 +33,7 @@ n'y a rien à copier. Rendons-nous dans le répertoire de compilation et lançon le makefile. #:~/git/olena/scribo/sandbox/green/demo/annotating/hsv$ -cd ../../../build/demo/annotating/hsv - +#:cd ../../../build/demo/annotating/hsv #:~/git/olena/scribo/sandbox/green/build/demo/annotating/hsv$ make clean all L'exécutable est généré par le makefile, il porte le nom du @@ -168,6 +167,14 @@ de comptage puisqu'il est décrit sous la forme de triplets de float contient une séquence d'appels pour les routines permettant de considérer la dimension de la teinte comme circulaire. +Après réflexion, le code des histogrammes et tous les accumulateurs +qui en découlent devraient être rangés dans l'espace de nommage +mln::accu::histo. Cela permettrait de ne pas parasiter mln::accu::stat +avec tous les éléments propres aux histogrammes et de ne pas faire un +espace de nommage à rallonge en introduisant histo dans stat. Donc +mln::accu::stat semble être une bonne postion pour le rangement final +du code relatif aux histogrammes. + a) version 1d @@ -198,7 +205,6 @@ c) version 3d RGB * use/accu/stat/histo3_rgb: Code minimal utilisant un histogramme 3d RGB. - d) version 3d HSL * mln/accu/stat/histo3d_hsl.hh: Accumulateur histogramme image3d HSL. @@ -245,23 +251,145 @@ IX KMEANS Ce travail m'avait été demandé par théo. Je le laisse inachevé, quelque part perdu pendant l'optimisation du code et sa transformation en canevas. + +a) Première implémentation avec matrices et vecteurs + +Cette version est bien documentée et permet de mieux comprendre les autres +versions. Par ailleurs, elle n'est pas spécialement optimisée ce qui fait +qu'elle colle davantage au modèle de l'algorithme kmean traditionnel. + * mln/clustering/k_mean.hh: Première implémentation avec matrices et vecteurs. +* use/clustering/k_mean: Code minimal utilisant cette première implémentation. +* tests/clustering/k_mean: Tests unitaires sur la permière version. + + +b) Seconde implémentation avec image en 1d + +Cette seconde version intègre une optimisation testée par théo il y a +longtemps. Je me demande si ce n'étais pas pendant sa thèse qu'il +avait travaillé sur cette version. Bref, dans la mesure où +l'optimisation passe par la création d'un histogramme, cette version +devient dépendante des histogrammes réalisés plutôt (il est aussi +dépendant de la sauvegarde au format gnuplot shell). L'idée générale +de l'optimisation est de ne plus raisonner sur l'image, mais sur +l'histogramme. Car à un moment donné, la classification ne tient +compte que de la valeur du pixel en intensité (pas de ses coordonnées +dans l'image). Donc tout pixel de même intensité sera classé de +manière identique. D'où l'intérêt de travailler avec les +histogrammes. Les limites de cette optimisation se trouvent dans la +taille des histogrammes. L'optimisation marche à merveille pour de +faibles quantifications (8 bits c'est parfait). Par contre, lorsque +l'on passe à un histogramme couleur, la taille de l'histogramme +devient problématique et peut dépasser la taille de l'image. Cette +optimisation n'a alors plus aucun sens. + * mln/clustering/kmean1d.hh: Implémentation 1d avec des images. +* use/clustering/kmean1d : Code minimal utilisant cette seconde implémentation. +* demo/clustering/kmean1d : Demonstrateur. + +La visualisation de la convergence des moyennes, générée par le +démonstrateur, n'est pas très lisible. La lecture textuelle du fichier +gnuplot shell (mean_cnv.sh) permet d'interpréter ce qui se passe, par +contre le graphique est à refaire. + + +c) kmean2d + +Cette troisième version est identique à la seconde, sauf qu'elle +permet de faire la classification dans un espace à deux +dimensions. Malheureusement, un tel travail dans cet espace coûte +beaucoup plus cher à l'exécution. + +* mln/fun/v2v/rg_to_rgb.hh: Transformation de l'espace RG vers l'espace RGB. +* use/fun/v2v/rg_to_rgb: Exemple de code pour l'utilisation de rg_to_rgb. + * mln/clustering/kmean2d.hh: Implémentation 2d avec des images. -* mln/clustering/kmean3d.hh: Implémentation 3d avec des images. -* mln/clustering/kmean_rgb.hh: Impl. 3d aplatie et en cours de mise en canevas. +* use/clustering/kmean2d : Code minimal utilisant cette seconde implémentation. +* demo/clustering/kmean2d : Demonstrateur. -* tests/clustering/k_mean: Tests unitaires sur la permière version. -* tests/clustering/kmean1d: Tests unitaire sur la version 1d. +La visualisation de la convergence des moyennes est cette fois-ci +complètement aboutie. Le fichier semble avoir quelques soucis, il +manque des lignes dans le splot initial, en remettre pour qu'il y en +ait autant que les blocs de données. L'espace des couleurs étant le +bicanal r/g, on peut visualiser sur une troisième dimension +l'évolution des centres dans l'espace initial. L'effet est très réussi +et aussi très parlant. Chaque run, correspond alors à une trajectoire +particulière. L'affichage des variations des variances a été rénové et +est lui aussi beaucoup plus lisible. + + +d) kmean3d + +Cette quatrième version est la copie conforme de la version +précédente. Les problèmes de visualisation sur les fichiés générés +sont les mêmes. L'affichage des convergences est identique. Le recours +à une dégradation de l'image d'entrée est impérative pour avoir des +temps encore acceptables. -* demo/clustering/kmean1d: Utilisation de la version 1d. -* demo/clustering/kmean2d: Utilisation de la version 2d. -* demo/clustering/kmean3d: Utilisation de la version 3d. +* mln/clustering/kmean3d.hh: Implémentation 3d avec des images. +* use/clustering/kmean3d : Code minimal utilisant cette quatrième impl. +* demo/clustering/kmean3d : Demonstrateur. + + +e) kmean aplati + +Cette cinquième version est très spéciale. Elle doit permettre de +gagner du temps d'exécution. Le concept est simple au départ, tout +écrire d'un seul tenant. L'exercice est fastidieux et difficile +(intellectuellement). Une fois fait, il faut réfléchir à ce qui peut +être mis sous forme de canevas. Par exemple, la manière de calculer +les distances peut être une information paramétrable. Cela pemettrait +de faire un benchmark in situ. La transcription actuelle compile. Elle +n'intègre pas tous les outils de debuggage que nous pouvions avoir sur +les autres versions. Par ailleurs, comme le code est réuni en une +seule fonction, nous avons pour l'instant une seule sortie, l'image de +labels de la classification réalisée. A partir de cette image, nous +pouvons en reconstruire d'autres. Il manque quand même la possibilité +d'observer les convergences. Le travail était en cours, à prendre donc +avec beaucoup de pincettes. La dernière exécution house.ppm, 3 +centres, 10 itérations et 10 runs fonctionne parfaitement sur les +premiers runs puis l'affichage s'emballe sans que l'on y comprenne +rien. Un dump dans un fichier révèle le non appariement de certaines +traces (entering/exiting). Une piste à suivre est la sortie anticipée +d'une de mes boucles sans pour autant fermer une trace ... ??? + +* mln/clustering/kmean_rgb.hh: Implémentation 3d avec des images. +* use/clustering/kmean_rgb : Code minimal utilisant cette quatrième impl. * demo/clustering/kmean_rgb: Utilisation de la version aplatie. + +f) optimisation possible + +Le calcul des distances entre les points et les différents centres +peut être réalisé par des transformées. Certes, les distances ne seront +pas les mêmes que la distance euclidienne, mais elles s'en approchent +et cela constitue très certainement une très bonne approximation pour +l'objectif que nous cherchons à atteindre. Le but de ce benchmark est +de regarder quel type de transformation est le plus rapide pour +arriver à nos fins en fonction des données d'entrée. Cette +optimisation n'a pas encore été intégrée dans le code, et reste une +piste à exploiter. + * bench/clustering/distance: Comparaison algorithmes d'évaluation des distances. -==> to do +Une routine du benchmark ne compile plus. Il semble qu'il y ait un +mauvais appel à la fonction at_ dans la routine +influence_zone_geodesic.hh ligne 127. Le but du benchmark est de +tester les distances en 2d et 3d pour des voisinages différents (c04, +c08, c06, c18, c26) sur les routines distance euclidienne classique, +zone d'influence geodesique et zone d'influence "front". La première +serie de test vise à garder une taille d' image constante et +d'augmenter le nombre de centres pour voir comment se comporte les +algorithmes et la seconde serie, vise à garder un nombre constant de +centres et à agrandir progressivement l'image. Attention, le benchmark +essaye d'être assez exhaustif et donc se paye par un temps d'execution +assez long. Les différents fichiers générés reprennent les différents +tests effectués et montrent l'évolution du temps de calcul suivant la +progression du paramètre observé (taille de l'image ou nombre de +centres). + +==> to do : le changelog + commit + give it to scribo-z branch + X REGIONAL MAXIMA ----------------- diff --git a/scribo/sandbox/green/bench/clustering/distance/Makefile.am b/scribo/sandbox/green/bench/clustering/distance/Makefile.am new file mode 100644 index 0000000..ca5e187 --- /dev/null +++ b/scribo/sandbox/green/bench/clustering/distance/Makefile.am @@ -0,0 +1,153 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/bench +BUILD__PATTERN= green/build/bench + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/scribo/sandbox/green/bench/clustering/distance/distance.cc b/scribo/sandbox/green/bench/clustering/distance/distance.cc new file mode 100644 index 0000000..4daea44 --- /dev/null +++ b/scribo/sandbox/green/bench/clustering/distance/distance.cc @@ -0,0 +1,842 @@ +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#include <mln/core/alias/box3d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/alias/point3d.hh> +#include <mln/core/alias/w_window2d_int.hh> +#include <mln/core/alias/w_window3d_int.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/fill.hh> + +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/zero.hh> + +#include <mln/make/w_window2d_int.hh> +#include <mln/make/w_window3d_int.hh> + +#include <mln/opt/at.hh> + +#include <mln/transform/influence_zone_geodesic.hh> +#include <mln/transform/influence_zone_front.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> + + +/// Build inputs, build seeds, count distance. +/// \{ +/// \brief Build 2d/3d input image and 2d/3d seed image. +/// +/// Building seeds means making a rectangular grid over a virtual image with +/// a specific size and collects together in a array each node of this grid. +/// In the context of the kmean algorithm, seeds are equavalent to centers. +/// Building input means creating a label image and associates to each seed +/// a unique label id. +/// Distance are square euclidian distance in 2d and in 3d. +mln::image2d<mln::value::label_8> build_input2d(unsigned nb_seed, unsigned size) +{ + typedef mln::value::label_8 t_lbl; + mln::image2d<t_lbl> input(mln::box2d(mln::point2d(0,0), + mln::point2d(size,size))); + + mln::data::fill(input, mln::literal::zero); + + unsigned top = size / nb_seed; + unsigned lbl = 0; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + input(mln::point2d(i,j)) = ++lbl; + + //mln::io::pgm::save(input, "input.pgm"); + + return input; +} + +mln::image3d<mln::value::label_8> build_input3d(unsigned nb_seed, unsigned size) +{ + typedef mln::value::label_8 t_lbl; + mln::image3d<t_lbl> input(mln::box3d(mln::point3d(0,0,0), + mln::point3d(size,size,size))); + + mln::data::fill(input, mln::literal::zero); + + unsigned top = size / nb_seed; + unsigned lbl = 0; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + for (unsigned k = top/2; k < size; k += top) + input(mln::point3d(k,i,j)) = ++lbl; + + //mln::io::pgm::save(input, "input.pgm"); + + return input; +} + +mln::util::array<mln::point3d> build_seed3d(unsigned nb_seed, unsigned size) +{ + mln::util::array<mln::point3d> result; + unsigned top = size / nb_seed; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + for (unsigned k = top/2; k < size; k += top) + result.append(mln::point3d(k,i,j)); + + // std::cout << result << std::endl; + + return result; +} + +mln::util::array<mln::point2d> build_seed2d(unsigned nb_seed, unsigned size) +{ + mln::util::array<mln::point2d> result; + unsigned top = size / nb_seed; + + for (unsigned i = top/2; i < size; i += top) + for (unsigned j = top/2; j < size; j += top) + result.append(mln::point2d(j,i)); + + // std::cout << result << std::endl; + + return result; +} + +unsigned distance(mln::point3d p1, mln::point3d p2) +{ + unsigned row2 = (p1.row() - p2.row())*(p1.row() - p2.row()); + unsigned col2 = (p1.col() - p2.col())*(p1.col() - p2.col()); + unsigned sli2 = (p1.sli() - p2.sli())*(p1.sli() - p2.sli()); + unsigned res = row2 + col2 + sli2; + + return res; +} + +unsigned distance(mln::point2d p1, mln::point2d p2) +{ + unsigned row2 = (p1.row() - p2.row())*(p1.row() - p2.row()); + unsigned col2 = (p1.col() - p2.col())*(p1.col() - p2.col()); + unsigned res = row2 + col2; + + return res; +} +/// \} + +/// Benchmark geodesic distance. +/// \{ +/// \brief The geodesic material for the benchmark process. +/// +/// This part of the file contains all we need to benchmarck the +/// geodesic distance. First of all, we have the calling functions +/// that encapsulate the real call to geodesic routine. They bring us +/// a clean place where starting and stopping the timer without any +/// other code interference. Printing results could be done at this step. +/// At least, we have the test routine that call the previous functions +/// by passing arguments like different size of inputs and different +/// neighbouring. The test routines save their results in gnuplot script shell +/// file that enable to show charts. +void influence_zone_geodesic_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::neighb2d & neighb) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_geodesic(input, neighb); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + +/// \fixme influence_zone_geodesic_3d doesn't compile! +void influence_zone_geodesic_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::neighb3d & neighb) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + // FIXME : Problem with the influence_zone_geodesic in 3d (bad call to at_). + // output = mln::transform::influence_zone_geodesic(input, neighb); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +int test_influence_zone_geodesic_2d() +{ + mln::util::timer timer; + +// Test the label c4 + mln::image1d<float> chart_seed_c04(16*16+2); + + mln::data::fill(chart_seed_c04, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_geodesic_2d(timer,build_input2d(i,256),mln::c4()); + std::cout << "c04|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c04, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c04, "geodesic.c04.2d.seed.sh"); + + // Test the label c8 + mln::image1d<float> chart_seed_c08(16*16+2); + + mln::data::fill(chart_seed_c08, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_geodesic_2d(timer,build_input2d(i,256),mln::c8()); + std::cout << "c08|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c08, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c08, "geodesic.c08.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c04(256+1); + + mln::data::fill(chart_size_c04, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_geodesic_2d(timer, build_input2d(4,i),mln::c4()); + std::cout << "c04|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c04, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c04, "geodesic.c04.2d.size.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c08(256+1); + + mln::data::fill(chart_size_c08, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_geodesic_2d(timer, build_input2d(4,i),mln::c8()); + std::cout << "c08|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c08, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c08, "geodesic.c08.2d.size.sh"); + + + return 0; +} + + +void test_influence_zone_geodesic_3d() +{ + mln::util::timer timer; + + // Test the number of labels c06 + mln::image1d<float> chart_seed_c06(5*5*5+2); + + mln::data::fill(chart_seed_c06, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c6()); + std::cout << "c06|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c06, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c06, "geodesic.c06.3d.seed.sh"); + + // Test the number of labels c18 + mln::image1d<float> chart_seed_c18(5*5*5+2); + + mln::data::fill(chart_seed_c18, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c18()); + std::cout << "c18|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c18, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c18, "geodesic.c18.3d.seed.sh"); + + // Test the number of labels c26 + mln::image1d<float> chart_seed_c26(5*5*5+2); + + mln::data::fill(chart_seed_c26, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(i,128), mln::c26()); + std::cout << "c26|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c26, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c26, "geodesic.c26.3d.seed.sh"); + + // Test the size of the image c06 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c06(128+1); + + mln::data::fill(chart_size_c06, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c6()); + std::cout << "c06|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c06, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c06, "geodesic.c06.3d.size.sh"); + + // Test the size of the image c18 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c18(128+1); + + mln::data::fill(chart_size_c18, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c18()); + std::cout << "c18|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c18, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c18, "geodesic.c18.3d.size.sh"); + + // Test the size of the image c26 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c26(128+1); + + mln::data::fill(chart_size_c26, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_geodesic_3d(timer, build_input3d(2,i), mln::c26()); + std::cout << "c26|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c26, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c26, "geodesic.c26.3d.size.sh"); +} +/// \} + +/// Benchmark front distance. +/// \{ +/// \brief The front material for the benchmark process. +/// +/// This part of the file contains all we need to benchmarck the +/// front distance. First of all, we have the calling functions +/// that encapsulate the real call to geodesic routine. They bring us +/// a clean place where starting and stopping the timer without any +/// other code interference. Printing results could be done at this step. +/// At least, we have the test routine that call the previous functions +/// by passing arguments like different size of inputs and different +/// neighbouring. The test routines save their results in gnuplot script shell +/// file that enable to show charts. +void influence_zone_front_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::neighb2d& neighb, + const mln::w_window2d_int& w2d) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_front(input, neighb, w2d); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void influence_zone_front_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::neighb3d& neighb, + const mln::w_window3d_int& w3d) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + output = mln::transform::influence_zone_front(input, neighb, w3d); + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void test_influence_zone_front_3d() +{ + mln::util::timer timer; + + int ws_c06[] = { + // Internal slice + 0, 0, 0, + 0, 2, 0, + 0, 0, 0, + // Middle slice + 0, 2, 0, + 2, 0, 2, + 0, 2, 0, + // External slice + 0, 0, 0, + 0, 2, 0, + 0, 0, 0 }; + + int ws_c18[] = { + // Internal slice + 0, 6, 0, + 6, 4, 6, + 0, 6, 0, + // Middle slice + 6, 4, 6, + 4, 0, 4, + 6, 4, 6, + // External slice + 0, 6, 0, + 6, 4, 6, + 0, 6, 0 }; + + int ws_c26[] = { + // Internal slice + 7, 6, 7, + 6, 4, 6, + 7, 6, 7, + // Middle slice + 6, 4, 6, + 4, 0, 4, + 6, 4, 6, + // External slice + 7, 6, 7, + 6, 4, 6, + 7, 6, 7 }; + + mln::w_window3d_int w3d_c06 = mln::make::w_window3d_int(ws_c06); + mln::w_window3d_int w3d_c18 = mln::make::w_window3d_int(ws_c18); + mln::w_window3d_int w3d_c26 = mln::make::w_window3d_int(ws_c26); + + // Test the number of labels c06 + mln::image1d<float> chart_seed_c06(5*5*5+2); + + mln::data::fill(chart_seed_c06, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c6(), w3d_c06); + std::cout << "c06|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c06, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c06, "front.c06.3d.seed.sh"); + + // Test the number of labels c18 + mln::image1d<float> chart_seed_c18(5*5*5+2); + + mln::data::fill(chart_seed_c18, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c18(), w3d_c18); + std::cout << "c18|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c18, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c18, "front.c18.3d.seed.sh"); + + // Test the number of labels c26 + mln::image1d<float> chart_seed_c26(5*5*5+2); + + mln::data::fill(chart_seed_c26, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(i,128), mln::c26(), w3d_c26); + std::cout << "c26|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c26, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c26, "front.c26.3d.seed.sh"); + + // Test the size of the image c06 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c06(128+1); + + mln::data::fill(chart_size_c06, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c6(), w3d_c06); + std::cout << "c06|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c06, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c06, "front.c06.3d.size.sh"); + + // Test the size of the image c18 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c18(128+1); + + mln::data::fill(chart_size_c18, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c18(), w3d_c18); + std::cout << "c18|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c18, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c18, "front.c18.3d.size.sh"); + + // Test the size of the image c26 + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c26(128+1); + + mln::data::fill(chart_size_c26, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_front_3d(timer, build_input3d(2,i), mln::c26(), w3d_c26); + std::cout << "c26|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c26, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c26, "front.c26.3d.size.sh"); +} + +void test_influence_zone_front_2d() +{ + mln::util::timer timer; + + int ws_c04[] = { + 0, 2, 0, + 2, 0, 2, + 0, 2, 0}; + + int ws_c08[] = { + 6, 4, 6, + 4, 0, 4, + 6, 4, 6}; + + mln::w_window2d_int w2d_c04 = mln::make::w_window2d_int(ws_c04); + mln::w_window2d_int w2d_c08 = mln::make::w_window2d_int(ws_c08); + + // Test the label c4 + mln::image1d<float> chart_seed_c04(16*16+2); + + mln::data::fill(chart_seed_c04, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_front_2d(timer,build_input2d(i,256),mln::c4(),w2d_c04); + std::cout << "c04|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c04, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c04, "front.c04.2d.seed.sh"); + + // Test the label c8 + mln::image1d<float> chart_seed_c08(16*16+2); + + mln::data::fill(chart_seed_c08, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_front_2d(timer,build_input2d(i,256),mln::c8(),w2d_c08); + std::cout << "c08|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed_c08, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed_c08, "front.c08.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c04(256+1); + + mln::data::fill(chart_size_c04, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_front_2d(timer, build_input2d(4,i),mln::c4(),w2d_c04); + std::cout << "c04|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c04, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c04, "front.c04.2d.size.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size_c08(256+1); + + mln::data::fill(chart_size_c08, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_front_2d(timer, build_input2d(4,i),mln::c8(),w2d_c08); + std::cout << "c08|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size_c08, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size_c08, "front.c08.2d.size.sh"); +} +/// \} + + + +/// Benchmark euclidian distance. +/// \{ +/// \brief The euclidian material for the benchmark process. +/// +/// This part of the file contains all we need to benchmarck the +/// euclidian distance. First of all, we have the calling functions +/// that encapsulate the real call to geodesic routine. They bring us +/// a clean place where starting and stopping the timer without any +/// other code interference. Printing results could be done at this step. +/// At least, we have the test routine that call the previous functions +/// by passing arguments like different size of inputs and different +/// neighbouring. The test routines save their results in gnuplot script shell +/// file that enable to show charts. +void influence_zone_euclidian_2d(mln::util::timer& timer, + const mln::image2d<mln::value::label_8>& input, + const mln::util::array<mln::point2d>& seed) +{ + mln::image2d<mln::value::label_8> output; + + timer.start(); + + mln::initialize(output, input); + + mln_piter_(mln::image2d<mln::value::label_8>) p(output.domain()); + mln_eiter_(mln::util::array<mln::point2d>) e(seed); + + for_all(p) + { + unsigned d = 0; + unsigned min_d = mln_max(unsigned); + unsigned min_l = 0; + + for_all(e) + { + d = distance(p, seed(e.index_())); + + if (min_d > d) + { + min_d = d; + min_l = input(e); + } + } + + output(p) = min_l; + } + + timer.stop(); + + //mln::io::pgm::save(output, "output.pgm"); +} + + +void influence_zone_euclidian_3d(mln::util::timer& timer, + const mln::image3d<mln::value::label_8>& input, + const mln::util::array<mln::point3d>& seed) +{ + mln::image3d<mln::value::label_8> output; + + timer.start(); + + mln::initialize(output, input); + + mln_piter_(mln::image3d<mln::value::label_8>) p(output.domain()); + mln_eiter_(mln::util::array<mln::point3d>) e(seed); + + for_all(p) + { + unsigned d = 0; + unsigned min_d = mln_max(unsigned); + unsigned min_l = 0; + + for_all(e) + { + d = distance(p, seed(e.index_())); + + if (min_d > d) + { + min_d = d; + min_l = input(e); + } + } + + output(p) = min_l; + } + + timer.stop(); +} + +void test_influence_zone_euclidian_2d() +{ + mln::util::timer timer; +/* + // Global test + mln::image2d<float> chart(mln::box2d(mln::point2d(0,0), + mln::point2d(16*16+2,256+1))); + + mln::data::fill(chart, mln::literal::zero); + + for (unsigned i = 1; i < 256+1; ++i) // size + for (unsigned j = 1; j < i*i && j < 16+1; ++j) // seed + { + influence_zone_euclidian_2d(timer,build_input2d(j,i),build_seed2d(j,i)); + std::cout << "xxx|" << i << "x" << i << "|" << j << " = " + << timer.read() << std::endl; + mln::opt::at(chart, j*j,i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart, "chart.sh"); +*/ + + // Test the number of labels + mln::image1d<float> chart_seed(16*16+2); + + mln::data::fill(chart_seed, mln::literal::zero); + + for (unsigned i = 1; i < 16+1; ++i) + { + influence_zone_euclidian_2d(timer,build_input2d(i,256),build_seed2d(i,256)); + std::cout << "xxx|256x256|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed, i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed, "euclidian.2d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size(256+1); + + mln::data::fill(chart_size, mln::literal::zero); + + for (unsigned i = 16; i < 256+1; ++i) + { + influence_zone_euclidian_2d(timer, build_input2d(4,i), build_seed2d(4,i)); + std::cout << "xxx|" << i << "x" << i << "|16 = " + << timer.read() << std::endl; + mln::opt::at(chart_size, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size, "euclidian.2d.size.sh"); +} + +void test_influence_zone_euclidian_3d() +{ + mln::util::timer timer; + + // Test the number of labels + mln::image1d<float> chart_seed(5*5*5+2); + + mln::data::fill(chart_seed, mln::literal::zero); + + for (unsigned i = 1; i < 5+1; ++i) + { + influence_zone_euclidian_3d(timer,build_input3d(i,128),build_seed3d(i,128)); + std::cout << "xxx|128x128x128|" << i << " = " << timer.read() << std::endl; + mln::opt::at(chart_seed, i*i*i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_seed, "euclidian.3d.seed.sh"); + + // Test the size of the image + // 1, 2, 4, 8, 16, 32, 64, 128, 256 + mln::image1d<float> chart_size(128+1); + + mln::data::fill(chart_size, mln::literal::zero); + + for (unsigned i = 6; i < 128+1; ++i) + { + influence_zone_euclidian_3d(timer, build_input3d(2,i), build_seed3d(2,i)); + std::cout << "xxx|" << i << "x" << i << "x" << i << "|8 = " + << timer.read() << std::endl; + mln::opt::at(chart_size, i) = timer.read(); + } + + mln::io::plot::save_image_sh(chart_size, "euclidian.3d.size.sh"); +} +/// \} + +//------------------------------------------------------------------------------ +// Main +// +// Point de fonctionnement du système [image de taille entre 1 et 256] +// Moins de 16 labels +// +//------------------------------------------------------------------------------ + +/// \brief The main routine that call all the benchmark tests. +/// +/// This benchmark aim at comparing different kind of distance. It +/// looks after the influence geodesic, the influence front and +/// euclidian distance. Many files are generated. Some rules exist to +/// build the name of the file: +/// distance.neighbouring.dimension.(size|seed). The distance could be +/// the front distance (front), the geodesic distance (geodesic) or +/// the euclidian distance (euclidian). The neighbouring could be c04 +/// or c08 in 2d, c06, c18 or c26 in 3d. The dimension could be 2d or 3d. +/// Finally, the flag size or seed precises the nature of that test, increasing +/// gradualy the size of the image or increasing gradualy the number of seeds. +/// The file contains the time benchmark of the distance studied. +/// +/// \fixme test_influence_zone_geodesic_3d doesn't compile. +int main() +{ + test_influence_zone_euclidian_2d(); + test_influence_zone_front_2d(); + test_influence_zone_geodesic_2d(); + + // FIXME : It doesn't compile because of a bad calling function at_ in + // FIXME : the influence_zone_geodesic.hh line 127. + // test_influence_zone_geodesic_3d(); + test_influence_zone_front_3d(); + test_influence_zone_euclidian_3d(); + + return 0; +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am new file mode 100644 index 0000000..4455a07 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am @@ -0,0 +1,153 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/kmean1d.cc b/scribo/sandbox/green/demo/clustering/kmean1d/kmean1d.cc new file mode 100644 index 0000000..22eae91 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean1d/kmean1d.cc @@ -0,0 +1,258 @@ +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief This is a demonstrator for kmean1d. +/// +/// The demonstrator produces five kind of results. The file +/// "color.ppm" allow us to localize the different center in the +/// image. There is an equivalent file "label.pgm" that's do the same +/// thing but inplace of a specific color, it gives the label of each +/// center. It's quit easy with xv, to transform the label image in +/// the color one. The file "mean.pgm" replaces each label by the mean +/// of the points that contributate to their center. It is a kind of +/// abstraction of the reality of the image, of its complexity. In +/// file "variance_cnv.sh" abscissa axis stands for the convergence +/// evolution and the ordinate axis for each runs. The "mean_cnv.sh" +/// file shows the evolution of the centers, whatever the run. The +/// x-axis represents the id of the center, the y-axis the iteration +/// in the convergence process and the z-axis the id of the particular +/// run. The graphic is very bad, but textual interpretation is still +/// available. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/clustering/kmean1d.hh> + +#include <mln/core/image/image2d.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/int_u8.hh> + + +/// \brief The real code for launching the kmean optimised algorithm. +/// +/// \param[in] image : the 8 bits greylevel image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// This is the real lauching code. In fact, the stuff consists in loading the +/// image, setting the parameters, calling the routine, extracting the results +/// and saving them for the user. +/// +/// \fixme The result is not safe because we don't test kmean.is_valid() + +void do_demo(const std::string& image, + const unsigned k_center, + const unsigned n_times, + const unsigned watch_dog) +{ + typedef mln::clustering::kmean1d<double,8> t_kmean; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + + t_image2d_int_u8 img; + + mln::io::pgm::load(img, image.c_str()); + + t_kmean kmean(img, + k_center, + watch_dog, + n_times); + + //mln::trace::quiet = false; + + kmean.launch_n_times(); + + // FIXME : Not safe because we don't test kmean.is_valid() + + t_kmean::t_color_dbg color_img = kmean.get_color_dbg(); + t_kmean::t_mean_dbg mean_img = kmean.get_mean_dbg(); + t_kmean::t_label_dbg label_img = kmean.get_label_dbg(); + t_kmean::t_variance_cnv var_cnv = kmean.get_variance_cnv(); + t_kmean::t_mean_cnv mean_cnv = kmean.get_mean_cnv(); + + mln::io::pgm::save(mean_img, "mean.pgm"); + mln::io::ppm::save(color_img, "color.ppm"); + mln::io::pgm::save(label_img, "label.pgm"); + + mln::io::plot::save_image_sh(mean_cnv, "mean_cnv.sh"); + mln::io::plot::save_image_sh(var_cnv, "variance_cnv.sh"); +} + + +/// \brief Front hand for the demonstrator. +/// +/// \param[in] image : the 8 bits greylevel image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// The front hand enables the setting of the default values for the +/// parameters. it allows us to print the real parameter set used by +/// the demonstrator. +void demo(const std::string& image = OLENA_IMG_PATH"/house.pgm", + const unsigned k_center = 3, + const unsigned n_times = 10, + const unsigned watch_dog = 10) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "Launching the demo with these parameters" << std::endl; + std::cout << "image : " << image << std::endl; + std::cout << "k_center : " << k_center << std::endl; + std::cout << "n_times : " << n_times << std::endl; + std::cout << "watch_dog : " << watch_dog << std::endl; + std::cout << "----------------------------------------" << std::endl; + + do_demo(image, k_center, n_times, watch_dog); +} + + +/// \brief Convert character get in the input stream to unsigned integer. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_unsigned(const bool status, const char *arg, unsigned& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + result = !arg_stream.fail(); + } + + return result; +} + + +/// \brief Convert character get in the input stream to string. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_string(const bool status, const char *arg, std::string& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + return !arg_stream.fail(); + } + + return result; +} + + +/// \brief Print usage recommandation for this binary. +/// +/// \param[in] argc : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +void usage(const int argc, const char *args[]) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "argc : " << argc << std::endl; + + for (int i = 0; i < argc; ++i) + std::cout << "args[" << i << "] : " << args[i] << std::endl; + + std::cout << "----------------------------------------" << std::endl; + std::cout << "usage: kmean1d [image [k_center [n_times [watch_dog]]]]" + << std::endl; + std::cout << "pbm image (points to work with)" << std::endl; + std::cout << "unsigned k_center (number of centers)" << std::endl; + std::cout << "unsigned n_times (number of launching)" << std::endl; + std::cout << "unsigned watch_dog (convergence loop)" << std::endl; + std::cout << "----------------------------------------" << std::endl; +} + + +/// \brief The main routine launchs the kmean optimised algoritm. +/// +/// \param[in] image : the 8 bits greylevel image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// All the parameters are optional, but we must respect strictly a +/// specific order. In fact, we can launch five programs with some +/// default parameters: demo(), demo(image), demo(image,k_center), +/// demo(image,k_center,n_times) and +/// demo(image,k_center,n_times,watch_dog). +int main(const int argc, const char *args[]) +{ + std::string image("top"); + unsigned k_center; + unsigned watch_dog; + unsigned n_times; + bool status = true; + + switch (argc) + { + case 5: status = char_to_unsigned(status, args[4], watch_dog); + case 4: status = char_to_unsigned(status, args[3], n_times); + case 3: status = char_to_unsigned(status, args[2], k_center); + case 2: status = char_to_string(status, args[1], image); break; + case 1: status = true; break; + default: status = false; + } + + if (status) + { + switch (argc) + { + case 1: demo(); break; + case 2: demo(image); break; + case 3: demo(image, k_center); break; + case 4: demo(image, k_center, n_times); break; + case 5: demo(image, k_center, n_times, watch_dog); break; + } + } + else + usage(argc, args); + + return 0; +} diff --git a/scribo/sandbox/green/demo/clustering/kmean2d/Makefile.am b/scribo/sandbox/green/demo/clustering/kmean2d/Makefile.am new file mode 100644 index 0000000..4455a07 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean2d/Makefile.am @@ -0,0 +1,153 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/scribo/sandbox/green/demo/clustering/kmean2d/kmean2d.cc b/scribo/sandbox/green/demo/clustering/kmean2d/kmean2d.cc new file mode 100644 index 0000000..2538dce --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean2d/kmean2d.cc @@ -0,0 +1,278 @@ +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief This is a demonstrator for kmean2d. +/// +/// The demonstrator produces five kind of results. The file +/// "color.ppm" allow us to localize the different center in the +/// image. There is an equivalent file "label.pgm" that's do the same +/// thing but inplace of a specific color, it gives the label of each +/// center. It's quit easy with xv, to transform the label image in +/// the color one. The file "mean.pgm" replaces each label by the mean +/// of the points that contributate to their center. It is a kind of +/// abstraction of the reality of the image, of its complexity. The +/// "variance_cnv.sh" changes its representation and becomes more +/// readable. The "mean_cnv.sh" have got a generation problem. We can +/// bypass it by adding printing lines in the 'splot' +/// instruction. However, the graphic is correct and mainly shows the +/// convergence of the center value. Finally, as the work takes place in +/// the r/g space, the "point.ppm" file shows us the exact input space. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/clustering/kmean2d.hh> + +#include <mln/core/image/image2d.hh> + +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb_to_rg.hh> +#include <mln/fun/v2v/rg_to_rgb.hh> + +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/rg.hh> +#include <mln/value/rgb8.hh> + + +/// \brief The real code for launching the kmean optimised algorithm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// This is the real lauching code. In fact, the stuff consists in loading the +/// image, setting the parameters, calling the routine, extracting the results +/// and saving them for the user. +/// +/// \fixme The result is not safe because we don't test kmean.is_valid() +void do_demo(const std::string& image, + const unsigned k_center, + const unsigned n_times, + const unsigned watch_dog) +{ + typedef mln::clustering::kmean2d<double,8> t_kmean; + typedef mln::value::rg<8> t_rg8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rg8> t_image2d_rg8; + typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; + typedef mln::fun::v2v::rg_to_rgb<8> t_rg_to_rgb; + + t_image2d_rgb8 img_rgb8; + t_image2d_rgb8 point_img_rgb8; + t_image2d_rgb8 mean_img_rgb8; + t_image2d_rgb8 mean_dbg_rgb8; + t_image2d_rg8 img_rg8; + + // Read input. + mln::io::ppm::load(img_rgb8, image.c_str()); + img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); + + // Call kmean. + t_kmean kmean(img_rg8, k_center, watch_dog, n_times); + + mln::trace::quiet = false; + kmean.launch_n_times(); + + // FIXME : Not safe because we don't test kmean.is_valid() + + // Get outputs. + t_kmean::t_point_img point_img = kmean.get_point(); + t_kmean::t_color_dbg color_img = kmean.get_color_dbg(); + t_kmean::t_mean_dbg mean_img = kmean.get_mean_dbg(); + t_kmean::t_label_dbg label_img = kmean.get_label_dbg(); + t_kmean::t_variance_cnv variance_cnv = kmean.get_variance_cnv(); + t_kmean::t_mean_cnv mean_cnv = kmean.get_mean_cnv(); + + // Convert outputs. + point_img_rgb8 = mln::data::transform(point_img, t_rg_to_rgb()); + mean_img_rgb8 = mln::data::transform(mean_img, t_rg_to_rgb()); + + mln::io::ppm::save(mean_img_rgb8, "mean.ppm"); + mln::io::ppm::save(color_img, "color.ppm"); + mln::io::pgm::save(label_img, "label.pgm"); + mln::io::ppm::save(point_img_rgb8, "point.ppm"); + + mln::io::plot::save_image_sh(mean_cnv, "mean_cnv.sh"); + mln::io::plot::save_image_sh(variance_cnv, "variance_cnv.sh"); +} + + +/// \brief Front hand for the demonstrator. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// The front hand enables the setting of the default values for the +/// parameters. it allows us to print the real parameter set used by +/// the demonstrator. +void demo(const std::string& image = OLENA_IMG_PATH"/house.ppm", + const unsigned k_center = 3, + const unsigned n_times = 10, + const unsigned watch_dog = 10) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "Launching the demo with these parameters" << std::endl; + std::cout << "image : " << image << std::endl; + std::cout << "k_center : " << k_center << std::endl; + std::cout << "n_times : " << n_times << std::endl; + std::cout << "watch_dog : " << watch_dog << std::endl; + std::cout << "----------------------------------------" << std::endl; + + do_demo(image, k_center, n_times, watch_dog); +} + + +/// \brief Convert character get in the input stream to unsigned integer. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_unsigned(const bool status, const char *arg, unsigned& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + result = !arg_stream.fail(); + } + + return result; +} + + +/// \brief Convert character get in the input stream to string. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_string(const bool status, const char *arg, std::string& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + return !arg_stream.fail(); + } + + return result; +} + + +/// \brief Print usage recommandation for this binary. +/// +/// \param[in] argc : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +void usage(const int argc, const char *args[]) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "argc : " << argc << std::endl; + + for (int i = 0; i < argc; ++i) + std::cout << "args[" << i << "] : " << args[i] << std::endl; + + std::cout << "----------------------------------------" << std::endl; + std::cout << "usage: kmean2d [image [k_center [n_times [watch_dog]]]]" + << std::endl; + std::cout << "ppm image (points to work with)" << std::endl; + std::cout << "unsigned k_center (number of centers)" << std::endl; + std::cout << "unsigned n_times (number of launching)" << std::endl; + std::cout << "unsigned watch_dog (convergence loop)" << std::endl; + std::cout << "----------------------------------------" << std::endl; +} + + +/// \brief The main routine launchs the kmean optimised algoritm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// All the parameters are optional, but we must respect strictly a +/// specific order. In fact, we can launch five programs with some +/// default parameters: demo(), demo(image), demo(image,k_center), +/// demo(image,k_center,n_times) and +/// demo(image,k_center,n_times,watch_dog). +int main(const int argc, const char *args[]) +{ + std::string image("top"); + unsigned k_center; + unsigned watch_dog; + unsigned n_times; + bool status = true; + + switch (argc) + { + case 5: status = char_to_unsigned(status, args[4], watch_dog); + case 4: status = char_to_unsigned(status, args[3], n_times); + case 3: status = char_to_unsigned(status, args[2], k_center); + case 2: status = char_to_string(status, args[1], image); break; + case 1: status = true; break; + default: status = false; + } + + if (status) + { + switch (argc) + { + case 1: demo(); break; + case 2: demo(image); break; + case 3: demo(image, k_center); break; + case 4: demo(image, k_center, n_times); break; + case 5: demo(image, k_center, n_times, watch_dog); break; + } + } + else + usage(argc, args); + + return 0; +} + diff --git a/scribo/sandbox/green/demo/clustering/kmean3d/Makefile.am b/scribo/sandbox/green/demo/clustering/kmean3d/Makefile.am new file mode 100644 index 0000000..4455a07 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean3d/Makefile.am @@ -0,0 +1,153 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/scribo/sandbox/green/demo/clustering/kmean3d/kmean3d.cc b/scribo/sandbox/green/demo/clustering/kmean3d/kmean3d.cc new file mode 100644 index 0000000..0e2b16e --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean3d/kmean3d.cc @@ -0,0 +1,265 @@ +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief This is a demonstrator for kmean3d. +/// +/// The demonstrator produces five kind of results. The file +/// "color.ppm" allow us to localize the different center in the +/// image. There is an equivalent file "label.pgm" that's do the same +/// thing but inplace of a specific color, it gives the label of each +/// center. It's quit easy with xv, to transform the label image in +/// the color one. The file "mean.pgm" replaces each label by the mean +/// of the points that contributate to their center. It is a kind of +/// abstraction of the reality of the image, of its complexity. The +/// "variance_cnv.sh" changes its representation and becomes more +/// readable. The "mean_cnv.sh" have got a generation problem. We can +/// bypass it by adding printing lines in the 'splot' +/// instruction. However, the graphic is correct and mainly shows the +/// convergence of the center value. + +#include <iostream> +#include <sstream> + +#include <mln/clustering/kmean3d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/img_path.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> + + +/// \brief The real code for launching the kmean optimised algorithm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// This is the real lauching code. In fact, the stuff consists in loading the +/// image, setting the parameters, calling the routine, extracting the results +/// and saving them for the user. +/// +/// \fixme The result is not safe because we don't test kmean.is_valid() +void do_demo(const std::string& image, + const unsigned k_center, + const unsigned n_times, + const unsigned watch_dog) +{ + typedef mln::clustering::kmean3d<double,5> t_kmean; + typedef mln::value::label_8 t_label_8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<5> t_rgb5; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb5> t_image2d_rgb5; + + t_image2d_rgb8 house_rgb8; + t_image2d_rgb5 house_rgb5; + + mln::io::ppm::load(house_rgb8, image.c_str()); + house_rgb5=mln::data::transform(house_rgb8,mln::fun::v2v::rgb8_to_rgbn<5>()); + + t_kmean kmean(house_rgb5, k_center, watch_dog, n_times); + + mln::trace::quiet = false; + + kmean.launch_n_times(); + + // FIXME : Not safe because we don't test kmean.is_valid() + + t_kmean::t_color_dbg color_img = kmean.get_color_dbg(); + t_kmean::t_mean_dbg mean_img = kmean.get_mean_dbg(); + t_kmean::t_label_dbg label_img = kmean.get_label_dbg(); + t_kmean::t_variance_cnv variance_cnv = kmean.get_variance_cnv(); + t_kmean::t_mean_cnv mean_cnv = kmean.get_mean_cnv(); + + mln::io::ppm::save(mean_img, "mean.ppm"); + mln::io::ppm::save(color_img, "color.ppm"); + mln::io::pgm::save(label_img, "label.pgm"); + + mln::io::plot::save_image_sh(mean_img, "mean.sh"); + mln::io::plot::save_image_sh(mean_cnv, "mean_cnv.sh"); + mln::io::plot::save_image_sh(variance_cnv, "variance_cnv.sh"); +} + +/// \brief Front hand for the demonstrator. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// The front hand enables the setting of the default values for the +/// parameters. it allows us to print the real parameter set used by +/// the demonstrator. +void demo(const std::string& image = OLENA_IMG_PATH"/house.ppm", + const unsigned k_center = 3, + const unsigned n_times = 10, + const unsigned watch_dog = 10) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "Launching the demo with these parameters" << std::endl; + std::cout << "image : " << image << std::endl; + std::cout << "k_center : " << k_center << std::endl; + std::cout << "n_times : " << n_times << std::endl; + std::cout << "watch_dog : " << watch_dog << std::endl; + std::cout << "----------------------------------------" << std::endl; + + do_demo(image, k_center, n_times, watch_dog); +} + + +/// \brief Convert character get in the input stream to unsigned integer. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_unsigned(const bool status, const char *arg, unsigned& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + result = !arg_stream.fail(); + } + + return result; +} + + +/// \brief Convert character get in the input stream to string. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_string(const bool status, const char *arg, std::string& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + return !arg_stream.fail(); + } + + return result; +} + + +/// \brief Print usage recommandation for this binary. +/// +/// \param[in] argc : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +void usage(const int argc, const char *args[]) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "argc : " << argc << std::endl; + + for (int i = 0; i < argc; ++i) + std::cout << "args[" << i << "] : " << args[i] << std::endl; + + std::cout << "----------------------------------------" << std::endl; + std::cout << "usage: kmean1d [image [k_center [n_times [watch_dog]]]]" + << std::endl; + std::cout << "pbm image (points to work with)" << std::endl; + std::cout << "unsigned k_center (number of centers)" << std::endl; + std::cout << "unsigned n_times (number of launching)" << std::endl; + std::cout << "unsigned watch_dog (convergence loop)" << std::endl; + std::cout << "----------------------------------------" << std::endl; +} + + +/// \brief The main routine launchs the kmean optimised algoritm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// All the parameters are optional, but we must respect strictly a +/// specific order. In fact, we can launch five programs with some +/// default parameters: demo(), demo(image), demo(image,k_center), +/// demo(image,k_center,n_times) and +/// demo(image,k_center,n_times,watch_dog). +int main(const int argc, const char *args[]) +{ + std::string image("top"); + unsigned k_center; + unsigned watch_dog; + unsigned n_times; + bool status = true; + + switch (argc) + { + case 5: status = char_to_unsigned(status, args[4], watch_dog); + case 4: status = char_to_unsigned(status, args[3], n_times); + case 3: status = char_to_unsigned(status, args[2], k_center); + case 2: status = char_to_string(status, args[1], image); break; + case 1: status = true; break; + default: status = false; + } + + if (status) + { + switch (argc) + { + case 1: demo(); break; + case 2: demo(image); break; + case 3: demo(image, k_center); break; + case 4: demo(image, k_center, n_times); break; + case 5: demo(image, k_center, n_times, watch_dog); break; + } + } + else + usage(argc, args); + + return 0; +} diff --git a/scribo/sandbox/green/demo/clustering/kmean_rgb/Makefile.am b/scribo/sandbox/green/demo/clustering/kmean_rgb/Makefile.am new file mode 100644 index 0000000..4455a07 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean_rgb/Makefile.am @@ -0,0 +1,153 @@ +# +# Generic Makefile +# + +######### +# TOOLS # +######### + +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) +ECHO= echo +RM= rm +MKDIR= mkdir -p +CP= cp + +SOURCE_PATTERN= green/demo +BUILD__PATTERN= green/build/demo + + +ifeq ($(findstring $(BUILD__PATTERN),$(PWD)), $(BUILD__PATTERN)) +# Case where make is done from build directory. +SOURCE_DIR= $(subst $(BUILD__PATTERN),$(SOURCE_PATTERN),$(PWD)) +BUILD__DIR= $(PWD)/ +else +# Case where make is done from source directory. +SOURCE_DIR= $(PWD)/ +BUILD__DIR= $(subst $(SOURCE_PATTERN),$(BUILD__PATTERN),$(PWD)) +endif + +SRC= $(notdir $(wildcard $(SOURCE_DIR)/*.cc)) +OLD= $(notdir $(wildcard $(SOURCE_DIR)/*~)) +OBJ= $(patsubst %.cc,%.o,$(SRC)) +SOURCE_MAKEFILE=Makefile.am +BUILD__MAKEFILE=Makefile +TARGET_FILE= $(notdir $(PWD)) +SOURCE_FILES= $(notdir $(wildcard $(SOURCE_DIR)/*.*)) +BUILD__FILES= $(filter-out $(SRC) $(SOURCE_MAKEFILE), $(SOURCE_FILES)) + +BUILD__F_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__FILES)) +SOURCE_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + +BUILD__M_PATH= $(addprefix $(BUILD__DIR)/,$(BUILD__MAKEFILE)) +SOURCE_M_PATH= $(addprefix $(SOURCE_DIR)/,$(SOURCE_MAKEFILE)) + +TARGET_F_PATH= $(addprefix $(BUILD__DIR)/,$(TARGET_FILE)) +OBJ_F_PATH= $(addprefix $(BUILD__DIR)/,$(OBJ)) +SRC_F_PATH= $(addprefix $(SOURCE_DIR)/,$(SRC)) +OLD_F_PATH= $(addprefix $(SOURCE_DIR)/,$(OLD)) + +############# +# BOOTSTRAP # +############# + + +bootstrap: $(BUILD__DIR) $(BUILD__F_PATH) $(BUILD__M_PATH) + +# Create, if nessary, the destination directory +$(BUILD__DIR): + $(MKDIR) $(BUILD__DIR) + +# Copy, if nessary, all the files, except the Makefile.am +$(BUILD__F_PATH): $(SOURCE_F_PATH) + $(CP) $(addprefix $(SOURCE_DIR),$(@F)) $@ + +# Copy if nessary, the Makefile.am into Makefile +$(BUILD__M_PATH): $(SOURCE_M_PATH) + $(CP) $(SOURCE_M_PATH) $(BUILD__M_PATH) + + +####### +# ALL # +####### + +# We assume that the call is done from the build directory. +# With the directive vpath, hidden files are found in the source directory. + +all: $(TARGET_F_PATH) + + +$(TARGET_F_PATH): $(OBJ_F_PATH) + $(LINK.cc) $< $(LOADLIBES) $(LDLIBS) -o $@ + +$(OBJ_F_PATH):$(SRC_F_PATH) + $(COMPILE.cc) $(OUTPUT_OPTION) $< + + +######### +# CLEAN # +######### + +# Force every time the deletion +clean: clean_target clean_obj clean_dst clean_old #clean_make + + +clean_target: + -@$(RM) $(TARGET_F_PATH) &> /dev/null + +clean_obj: + -@$(RM) $(OBJ_F_PATH) &> /dev/null + +clean_dst: + -@$(RM) $(BUILD_F_PATH) &> /dev/null + +clean_make: + -@$(RM) $(BUILD_M_PATH) &> /dev/null + +clean_old: + -@$(RM) $(OLD_F_PATH) &> /dev/null + + +######### +# PRINT # +######### + +print: print_tools print_bootstrap + +print_tools: + @$(ECHO) "HOME = $(HOME)" + @$(ECHO) "INCLUDES = $(INCLUDES)" + @$(ECHO) "CXXFLAGS = $(CXXFLAGS)" + @$(ECHO) "ECHO = $(ECHO)" + @$(ECHO) "RM = $(RM)" + @$(ECHO) "MKDIR = $(MKDIR)" + @$(ECHO) "CP = $(CP)" + @$(ECHO) + +print_bootstrap: + @$(ECHO) "PWD = $(PWD)" + @$(ECHO) "SOURCE_PATTERN = $(SOURCE_PATTERN)" + @$(ECHO) "BUILD__PATTERN = $(BUILD__PATTERN)" + @$(ECHO) "SOURCE_DIR = $(SOURCE_DIR)" + @$(ECHO) "BUILD__DIR = $(BUILD__DIR)" + @$(ECHO) "SOURCE_MAKEFILE = $(SOURCE_MAKEFILE)" + @$(ECHO) "BUILD__MAKEFILE = $(BUILD__MAKEFILE)" + @$(ECHO) "TARGET_FILE = $(TARGET_FILE)" + @$(ECHO) "SOURCE_FILES = $(SOURCE_FILES)" + @$(ECHO) "SOURCE_F_PATH = $(SOURCE_F_PATH)" + @$(ECHO) "BUILD__FILES = $(BUILD__FILES)" + @$(ECHO) "BUILD__F_PATH = $(BUILD__F_PATH)" + @$(ECHO) "BUILD__M_PATH = $(BUILD__M_PATH)" + @$(ECHO) "SOURCE_M_PATH = $(SOURCE_M_PATH)" + @$(ECHO) "SRC = $(SRC)" + @$(ECHO) "OBJ = $(OBJ)" + @$(ECHO) "OLD = $(OLD)" + @$(ECHO) "SRC_F_PATH = $(SRC_F_PATH)" + @$(ECHO) "OBJ_F_PATH = $(OBJ_F_PATH)" + @$(ECHO) "OLD_F_PATH = $(OLD_F_PATH)" + @$(ECHO) diff --git a/scribo/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc b/scribo/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc new file mode 100644 index 0000000..d02c497 --- /dev/null +++ b/scribo/sandbox/green/demo/clustering/kmean_rgb/kmean_rgb.cc @@ -0,0 +1,239 @@ +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief This is a demonstrator for kmean_rgb. +/// +/// The code is provided as is. It is an alpha version, so very very +/// experimental. Take care !! + +#include <iostream> +#include <sstream> + +#include <mln/clustering/kmean_rgb.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/img_path.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> + + +/// \brief The real code for launching the kmean optimised algorithm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// This is the real lauching code. In fact, the stuff consists in loading the +/// image, setting the parameters, calling the routine, extracting the results +/// and saving them for the user. +/// +/// \fixme The result is not safe because we don't test kmean.is_valid() +void do_demo(const std::string& image, + const unsigned k_center, + const unsigned n_times, + const unsigned watch_dog) +{ + typedef mln::value::label_8 t_lbl8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<5> t_rgb5; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb5> t_image2d_rgb5; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + + t_image2d_rgb8 img_rgb8; + t_image2d_rgb5 img_rgb5; + t_image2d_lbl8 img_lbl8; + + mln::io::ppm::load(img_rgb8, image.c_str()); + img_rgb5 = mln::data::transform(img_rgb8,mln::fun::v2v::rgb8_to_rgbn<5>()); + img_lbl8 = mln::clustering::kmean_rgb<double,5>(img_rgb5, + k_center, + watch_dog, + n_times); + + mln::io::pgm::save(img_lbl8, "label.pgm"); +} + +/// \brief Front hand for the demonstrator. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// The front hand enables the setting of the default values for the +/// parameters. it allows us to print the real parameter set used by +/// the demonstrator. +void demo(const std::string& image = OLENA_IMG_PATH"/house.ppm", + const unsigned k_center = 3, + const unsigned n_times = 3, + const unsigned watch_dog = 3) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "Launching the demo with these parameters" << std::endl; + std::cout << "image : " << image << std::endl; + std::cout << "k_center : " << k_center << std::endl; + std::cout << "n_times : " << n_times << std::endl; + std::cout << "watch_dog : " << watch_dog << std::endl; + std::cout << "----------------------------------------" << std::endl; + + do_demo(image, k_center, n_times, watch_dog); +} + + +/// \brief Convert character get in the input stream to unsigned integer. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_unsigned(const bool status, const char *arg, unsigned& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + result = !arg_stream.fail(); + } + + return result; +} + + +/// \brief Convert character get in the input stream to string. +/// +/// \param[in] status : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +/// \param[out] val : the converted value to output. +/// +/// \return a flag that tell us if every thing is ok, if the result can be used. +bool char_to_string(const bool status, const char *arg, std::string& val) +{ + bool result = false; + + if (status) + { + std::istringstream arg_stream(arg); + + arg_stream >> val; + + return !arg_stream.fail(); + } + + return result; +} + + +/// \brief Print usage recommandation for this binary. +/// +/// \param[in] argc : the exiting flag tell us to exit without doing any job. +/// \param[in] arg : the input argument to convert. +void usage(const int argc, const char *args[]) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "argc : " << argc << std::endl; + + for (int i = 0; i < argc; ++i) + std::cout << "args[" << i << "] : " << args[i] << std::endl; + + std::cout << "----------------------------------------" << std::endl; + std::cout << "usage: kmean1d [image [k_center [n_times [watch_dog]]]]" + << std::endl; + std::cout << "pbm image (points to work with)" << std::endl; + std::cout << "unsigned k_center (number of centers)" << std::endl; + std::cout << "unsigned n_times (number of launching)" << std::endl; + std::cout << "unsigned watch_dog (convergence loop)" << std::endl; + std::cout << "----------------------------------------" << std::endl; +} + + +/// \brief The main routine launchs the kmean optimised algoritm. +/// +/// \param[in] image : the color image. +/// \param[in] k_center : the number of centers. +/// \param[in] n_times : the number of launching. +/// \param[in] watch_dog : the number of iterations to manage the convergence. +/// +/// All the parameters are optional, but we must respect strictly a +/// specific order. In fact, we can launch five programs with some +/// default parameters: demo(), demo(image), demo(image,k_center), +/// demo(image,k_center,n_times) and +/// demo(image,k_center,n_times,watch_dog). +int main(const int argc, const char *args[]) +{ + std::string image("top"); + unsigned k_center; + unsigned watch_dog; + unsigned n_times; + bool status = true; + + switch (argc) + { + case 5: status = char_to_unsigned(status, args[4], watch_dog); + case 4: status = char_to_unsigned(status, args[3], n_times); + case 3: status = char_to_unsigned(status, args[2], k_center); + case 2: status = char_to_string(status, args[1], image); break; + case 1: status = true; break; + default: status = false; + } + + if (status) + { + switch (argc) + { + case 1: demo(); break; + case 2: demo(image); break; + case 3: demo(image, k_center); break; + case 4: demo(image, k_center, n_times); break; + case 5: demo(image, k_center, n_times, watch_dog); break; + } + } + else + usage(argc, args); + + return 0; +} diff --git a/scribo/sandbox/green/mln/clustering/k_mean.hh b/scribo/sandbox/green/mln/clustering/k_mean.hh index db3f34c..74acda7 100644 --- a/scribo/sandbox/green/mln/clustering/k_mean.hh +++ b/scribo/sandbox/green/mln/clustering/k_mean.hh @@ -1,6 +1,4 @@ -// Copyright (C) 2007 EPITA Research and Development Laboratory (LRDE) -// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) -// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -26,43 +24,89 @@ // executable file might be covered by the GNU General Public License. #ifndef MLN_CLUSTERING_K_MEAN_HH -#define MLN_CLUSTERING_K_MEAN_HH +# define MLN_CLUSTERING_K_MEAN_HH /// \file /// -/// \brief Implements the K MEAN algorithm. -/// DO -/// - ASSIGNEMENT STEP -/// - UPDATE STEP -/// LOOP UNTIL CONVERGENCE +/// \brief Implement the K MEAN algorithm with matrix and vectors. /// -/// ASSIGNEMENT STEP +/// This is the first impementation of the kmean algorithm. There is no +/// specific optimisation but every part of the algorithm is here and +/// it works. As it used vectors and matrices, it is not very convenient to +/// work with this version. +/// +/// \verbatim +/// This is the main lines of the kmean algorithm: +/// +/// PROCEDURE MAIN +/// BEGIN +/// DO +/// - CALL ASSIGNEMENT STEP +/// - CALL UPDATE STEP +/// LOOP UNTIL CONVERGENCE +/// END +/// +/// PROCEDURE ASSIGNEMENT STEP /// BEGIN -/// - COMPUTE THE DISTANCE MATRIX FROM POINTS TO CENTERS -/// - COMPUTE GROUP MATRIX WHERE EACH POINT IS ASSIGNED TO NEAREST CENTER +/// - COMPUTE THE DISTANCE MATRIX FROM POINTS TO CENTERS +/// - COMPUTE GROUP MATRIX WHERE EACH POINT IS ASSIGNED TO THE NEAREST CENTER /// END /// -/// UPDATE STEP +/// PROCEDURE UPDATE STEP /// BEGIN -/// - COMPUTE THE MEAN OF THE GROUPS WHICH ARE THE NEW CENTERS +/// - COMPUTE THE MEAN OF THE GROUPS WHICH ARE NOW THE NEW CENTERS /// END +/// \endvarbitim +/// +/// The following sample is the standard use of this first +/// implementation. The number of centers is a decision left to the +/// final user. As the representation uses vectors and matices, size +/// of each objects must be known at compile time, and so NB_POINT must be +/// a constant expression (mln::geom::nsites couldn't be called). +/// +/// #include <mln/clustering/k_mean.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/ppm/load.hh> +/// #include <mln/trait/value_.hh> +/// #include <mln/value/rgb8.hh> +/// +/// #define NB_CENTER 9 +/// #define NB_POINT (128*128) +/// #define POINT_SIZE mln_dim(mln::value::rgb8) +/// #define POINT_TYPE double +/// +/// int main() +/// { +/// typedef mln::value::rgb8 t_rgb8; +/// mln::image2d<t_rgb8> img_rgb8; +/// +/// mln::io::ppm::load(img_ref, OLENA_IMG_PATH"/house.ppm"); +/// mln::clustering::k_mean<NB_POINT,NB_CENTER,POINT_SIZE,POINT_TYPE> kmean; +/// +/// kmean.init_point(img_rgb8); +/// kmean.loop(img_rgb8); +/// +/// return 0; +/// } + +# include <limits.h> +# include <iostream> -#include <limits.h> -#include <iostream> -#include <mln/trace/entering.hh> -#include <mln/trace/exiting.hh> +# include <mln/algebra/mat.hh> +# include <mln/algebra/vec.hh> -#include <mln/core/contract.hh> -#include <mln/trait/value_.hh> +# include <mln/core/concept/image.hh> +# include <mln/core/contract.hh> +# include <mln/core/macros.hh> -#include <mln/algebra/mat.hh> -#include <mln/algebra/vec.hh> +# include <mln/math/min.hh> +# include <mln/norm/l2.hh> -#include <mln/math/min.hh> -#include <mln/norm/l2.hh> +# include <mln/trace/entering.hh> +# include <mln/trace/exiting.hh> -#include <mln/core/concept/image.hh> -#include <mln/core/macros.hh> +# include <mln/trait/value_.hh> namespace mln { @@ -78,149 +122,221 @@ namespace mln namespace clustering { - - // - // PARAM : DIM VECTOR, NUMBER OF CLASSES - // INPUT : MATRIX 1..N (SAMPLE) - // WORK - // - - // n is the size of the sample. - // k is the number of centers. - // p is the number of attributes for a point. - // T is the type for computations. + + /// \brief Implements the K MEAN algorithm with matrix and vectors. + /// + /// Param n is the size of the sample (the number of point to be + /// classified). Param k is the number of centers, at the end, + /// each point is link to one the nearest center. Param p is the + /// number of attributes of a point, in fact its dimension (int_u8 + /// = 1, rgb8 = 3, etc ...). Param T is the type used for + /// computations, the type of the point, typiccaly float or + /// double. + /// + /// \ingroup modclustering template <unsigned n, unsigned k, unsigned p, typename T> struct k_mean { - //------------------------------------------------------------------------ - // Constructor and destructor - //------------------------------------------------------------------------ - + /// Constructors and destructors. + /// \{ + /// \brief Allocate or deallocate the data structure. + /// + /// Allocate or deallocate the _point, _distance, _group, + /// _center, _variance data structure with the correct size. k_mean(); ~k_mean(); + /// \} - //------------------------------------------------------------------------ - // Accessors - //------------------------------------------------------------------------ - + /// Accessors. + /// \{ + /// \brief Hack to view temporary results of the kmean algorithm. + /// + /// These methods are necessary for unitary testing and debugging. algebra::mat<n, p, T>& get_point(); algebra::mat<k, p, T>& get_center(); algebra::mat<n, k, T>& get_distance(); algebra::mat<n, k, T>& get_group(); algebra::vec<k, T>& get_variance(); + /// \} - - //------------------------------------------------------------------------ - // Tools - //------------------------------------------------------------------------ + /// Tools. + /// \{ + /// \brief Define elementary function that are not available from milena. + /// + /// These methods are not interesting from the kmean'point of + /// view but are required for the gluing process. - /// \brief Return the euclidian distance beetween vector x and vector y. + /// \brief Return the euclidian distance beetween two vectors x and y. /// /// Compute the f$\sqrt(\sum_{i=0}^P ((x_i - y_i)^2)$f /// - /// \param[in] x a vector of dimension 1xP. - /// \param[in] y a second vector of dimension 1xP. + /// \param[in] x a vector of dimension p. + /// \param[in] y a second vector of dimension p. /// \return the distance between x and y. - mln_sum_product(T,T) euclidian_distance(const algebra::vec<p,T>& x, const algebra::vec<p,T>& y) const; - /// \brief Return the ith column. - /// - /// \param[in] m a matrix of dimension {N or K}xP. - /// \return the ith column as a vector of dimension 1xP. - - /// \brief Return the ith column. + /// \brief Return a stack copy of the ith column. /// - /// \param[in] m a matrix of dimension {N or K}xP. - /// \return the ith column as a vector of dimension 1xP. + /// \param[in] m a matrix of dimension r x q. + /// \param[in] _col the index of the selected column. + /// \return a stack copy of the ith column as a vector of dimension q. template <unsigned q, unsigned r> algebra::vec<q,T> col(const algebra::mat<r, q, T>& m, const unsigned _col) const; + + /// \brief Return a heap copy of the ith col. + /// + /// \param[in] m a matrix of dimension r x q. + /// \param[in] _col the index of the selected column. + /// \return a heap copy of the ith column as a vector of dimension q. template <unsigned q, unsigned r> algebra::vec<q,T>* ptr_col(const algebra::mat<r, q, T>& m, const unsigned _col) const; + + /// \brief Return a stack copy of the ith row. + /// + /// \param[in] m a matrix of dimension r x q. + /// \param[in] _row the index of the selected row. + /// \return a stack copy of the ith row as a vector of dimension q. template <unsigned q, unsigned r> algebra::vec<r,T> row(const algebra::mat<r, q, T>& m, const unsigned _row) const; + + /// \brief Divide inplace a column of the matrix by a scalar. + /// + /// \param[in] m a matrix of dimension r x q. + /// \param[in] _col the index of the selected column. + /// \param[in] value the scalar by which dividing the column. template <unsigned q, unsigned r> void div_col(algebra::mat<r, q, T>& m, const unsigned _col, const T value); + + /// \brief Sum all the elements of one row. + /// + /// \param[in] m a matrix of dimension r x q. + /// \param[in] _row the index of the selected row. + /// \return the sum of every attributes of the row. template <unsigned q, unsigned r> mln_sum(T) sum_row(const algebra::mat<r, q, T>& m, const unsigned _row) const; + + /// \brief Return the minimum of a vector. + /// + /// \param[in] x a vector of dimension q. + /// \return the mininum of that input vector. template <unsigned q, typename M> M min_col(const algebra::vec<q, M>& x) const; + /// \} - //------------------------------------------------------------------------ - // Initializations of points and centers - //------------------------------------------------------------------------ - - /// \brief Initialize the matrix _point with the pixels of an image. + /// Initializations. + /// \{ + /// \brief Initialize the _point and _center data structure. /// - /// \param[in] input an image which contains the data points. + /// The _point data structure is fed with the value of the input + /// image. The _center data structure is fed with random points + /// taken from the input image. template <typename I> void init_point(const Image<I>& _input); void init_center(); + /// \} - - //------------------------------------------------------------------------ - // Computations of distance, group, center, within variance - //------------------------------------------------------------------------ + /// Computations. + /// \{ + /// \brief Define the core routines of the kmean algorithm. + /// + /// The update_distance, update_group, update_center and update_variance + /// define the core kmean routines. If some optimization muste be done, + /// one may looks here. + + + /// \brief Update the _distance data structure. + /// + /// Compute the square distance between each point of the data + /// cloud and each center. void update_distance(); + + /// \brief Update the _group data structure. + /// + /// For each point of the data cloud, find the group which has + /// the minimum distance from it. void update_group(); - void update_center(); - T update_variance(); + /// \brief Update the _center data structure. + /// + /// For each group, compute the center of mass in therm of + /// points of the data cloud. + void update_center(); - //------------------------------------------------------------------------ - // Main loop - //------------------------------------------------------------------------ + /// \brief Update the _variance. + /// + /// Compute the within variance. Sum every shortest distance. + T update_variance(); + /// \} + /// \brief Define the orchestration of the kmean core routines. + /// + /// \param[in] _input the initial image which initialize the data cloud. + /// + /// The orchestration is in a fact a loop which call one after + /// each other the core routines. Ending the loop means managing + /// the statistical convergence or having some clues of + /// divergence (watch_dog). template <typename I> void loop(const Image<I>& _input); - //void loop(); private: + /// \brief watch_dog define the maximum of iterations that can be done. + /// + /// That constant help us to decide if divergence has been + /// occurred or not. static const unsigned watch_dog = 100; - + + /// \brief _points contains the concatenation of every data points. /// - /// One data point is a vector 1xP where P is the number of attributes. - /// _points is a matrix NxP where N is the number of data points. + /// One data point is a vector 1 x p where p is the number of attributes. + /// So _points is a matrix n x P where n is the number of data points. algebra::mat<n, p, T>* _point; + /// \brief _distance contains all the euclidian distances between points /// and the centers. /// - /// _distance is a matrix NxK where N is the number of data points and - /// K the number of centers. + /// _distance is a matrix n x k where n is the number of data points and + /// k the number of centers. algebra::mat<n, k, mln_sum_product(T,T)>* _distance; + /// \brief _group contains the point assignement to one center. /// - /// _group is a boolean matrix NxK where N is the number of data points - /// and K the number of centers. + /// _group is a boolean matrix n x k where n is the number of data points + /// and k the number of centers. algebra::mat<n, k, T>* _group; - /// \brief _center contains the coordonnate of the K centers. + + /// \brief _center contains the coordonnate of the k centers. /// - /// _center is a matrix KxP where K is the number of centers and P is the - /// number of attributes. + /// _center is a matrix k x p where k is the number of centers + /// and p is the number of attributes. algebra::mat<k, p, T>* _center; + + /// \brief _variance contains the variance of each group. + /// + /// _variance is a vector 1 x k where k is the number of centers. algebra::vec<k, T>* _variance; }; @@ -234,7 +350,7 @@ namespace mln inline k_mean<n,k,p,T>::k_mean() { - trace::entering("mln::clustering::k_mean::k_mean"); + trace::entering("mln::clustering::k_mean::cstor"); _point = new algebra::mat<n, p, T>(); _distance = new algebra::mat<n, k, mln_sum_product(T,T)>(); @@ -248,14 +364,14 @@ namespace mln mln_postcondition(_center != 0); mln_postcondition(_variance != 0); - trace::exiting("mln::clustering::k_mean::k_mean"); + trace::exiting("mln::clustering::k_mean::cstor"); } template <unsigned n, unsigned k, unsigned p, typename T> inline k_mean<n,k,p,T>::~k_mean() { - trace::entering("mln::clustering::k_mean::~k_mean"); + trace::entering("mln::clustering::k_mean::dstor"); delete _point; delete _distance; @@ -263,7 +379,7 @@ namespace mln delete _center; delete _variance; - trace::exiting("mln::clustering::k_mean::~k_mean"); + trace::exiting("mln::clustering::k_mean::dstor"); } //-------------------------------------------------------------------------- @@ -350,7 +466,7 @@ namespace mln template <unsigned n, unsigned k, unsigned p, typename T> template <unsigned q, unsigned r> inline - algebra::vec<q,T> k_mean<n,k,p,T>::col(const algebra::mat<r, q, T>& m, + algebra::vec<q,T> k_mean<n,k,p,T>::col(const algebra::mat<r, q, T>& m, const unsigned _col) const { trace::entering("mln::clustering::k_mean::col"); @@ -368,7 +484,7 @@ namespace mln template <unsigned n, unsigned k, unsigned p, typename T> template <unsigned q, unsigned r> inline - algebra::vec<q,T>* k_mean<n,k,p,T>::ptr_col(const algebra::mat<r, q, T>& m, + algebra::vec<q,T>* k_mean<n,k,p,T>::ptr_col(const algebra::mat<r, q, T>& m, const unsigned _col) const { trace::entering("mln::clustering::k_mean::ptr_col"); @@ -386,7 +502,7 @@ namespace mln template <unsigned n, unsigned k, unsigned p, typename T> template <unsigned q, unsigned r> inline - mln_sum(T) k_mean<n,k,p,T>::sum_row(const algebra::mat<r, q, T>& m, + mln_sum(T) k_mean<n,k,p,T>::sum_row(const algebra::mat<r, q, T>& m, const unsigned _row) const { trace::entering("mln::clustering::k_mean::sum_row"); @@ -404,7 +520,7 @@ namespace mln template <unsigned n, unsigned k, unsigned p, typename T> template <unsigned q, unsigned r> inline - void k_mean<n,k,p,T>::div_col(algebra::mat<r, q, T>& m, + void k_mean<n,k,p,T>::div_col(algebra::mat<r, q, T>& m, const unsigned _col, const T value) { @@ -422,21 +538,21 @@ namespace mln inline M k_mean<n,k,p,T>::min_col(const algebra::vec<q, M>& x) const { - trace::entering("mln::clustering::k_mean<n,k,p,T>::min_col"); - + trace::entering("mln::clustering::k_mean::min_col"); + M result = x[0]; for (unsigned i = 1; i < q; ++i) result = math::min(result, x[i]); - trace::exiting("mln::clustering::k_mean<n,k,p,T>::min_col"); + trace::exiting("mln::clustering::k_mean::min_col"); return result; } template <unsigned n, unsigned k, unsigned p, typename T> inline mln_sum_product(T,T) - k_mean<n,k,p,T>::euclidian_distance(const algebra::vec<p, T>& x, + k_mean<n,k,p,T>::euclidian_distance(const algebra::vec<p, T>& x, const algebra::vec<p, T>& y) const { trace::entering("mln::clustering::k_mean::euclidian_distance"); @@ -458,8 +574,8 @@ namespace mln inline void k_mean<n,k,p,T>::init_point(const Image<I>& _input) { - trace::entering("mln::clustering::k_mean<n,k,p,T>::init"); - + trace::entering("mln::clustering::k_mean::init"); + const I& input = exact(_input); algebra::mat<n, p, T>& point = *_point; @@ -474,28 +590,28 @@ namespace mln for_all(pi) { //std::cout << pi << std::endl; - + ++i; for (unsigned j = 0; j < p; ++j) { - + point(i,j) = input(pi).comp(j); //point(i,j) = input(pi); } } - trace::exiting("mln::clustering::k_mean<n,k,p,T>::init"); + trace::exiting("mln::clustering::k_mean::init"); } template <unsigned n, unsigned k, unsigned p, typename T> inline void k_mean<n,k,p,T>::init_center() { - trace::entering("mln::clustering::k_mean<n,k,p,T>::init_center"); - + trace::entering("mln::clustering::k_mean::init_center"); + algebra::mat<n, p, T>& point = *_point; algebra::mat<k, p, T>& center = *_center; - + // A random point is choosen to be the initial value for a center. for (unsigned i = 0; i < k; ++i) { @@ -503,16 +619,16 @@ namespace mln for (unsigned j = 0; j < p; ++j) { - center(i,j) = point(random, j); + center(i,j) = point(random, j); } //std::cout << "center(" << i << ")" << col(center, i) << std::endl; } - trace::exiting("mln::clustering::k_mean<n,k,p,T>::init_center"); + trace::exiting("mln::clustering::k_mean::init_center"); } - + //-------------------------------------------------------------------------- // Computations of distance, group, center, within variance //-------------------------------------------------------------------------- @@ -528,7 +644,7 @@ namespace mln algebra::mat<n, p, T>& point = *_point; algebra::mat<n, k, T>& distance = *_distance; algebra::mat<k, p, T>& center = *_center; - + // for all points in the data cloud. for (unsigned i = 0; i < n; ++i) { @@ -546,7 +662,7 @@ namespace mln inline void k_mean<n,k,p,T>::update_group() { - trace::entering("mln::clustering::k_mean<n,k,p,T>::update_group"); + trace::entering("mln::clustering::k_mean::update_group"); algebra::mat<n, k, mln_sum_product(T,T)>& distance = *_distance; algebra::mat<n, k, T>& group = *_group; @@ -563,14 +679,14 @@ namespace mln } // mln_postcondition(sum(col(distance(i,j)) == 1) Vi - trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_group"); + trace::exiting("mln::clustering::k_mean::update_group"); } template <unsigned n, unsigned k, unsigned p, typename T> inline void k_mean<n,k,p,T>::update_center() { - trace::entering("mln::clustering::k_mean<n,k,p,T>::update_center"); + trace::entering("mln::clustering::k_mean::update_center"); algebra::mat<n, p, T>& point = *_point; algebra::mat<k, p, T>& center = *_center; @@ -592,14 +708,14 @@ namespace mln for (unsigned i = 0; i < k; ++i) div_col(center, i, sum_row(group, i)); - trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_center"); + trace::exiting("mln::clustering::k_mean::update_center"); } template <unsigned n, unsigned k, unsigned p, typename T> inline T k_mean<n,k,p,T>::update_variance() { - trace::entering("mln::clustering::k_mean<n,k,p,T>::update_variance"); + trace::entering("mln::clustering::k_mean::update_variance"); algebra::vec<k, T>& variance = *_variance; algebra::mat<n, k, T>& distance = *_distance; @@ -613,10 +729,10 @@ namespace mln result += variance[i]; } - trace::exiting("mln::clustering::k_mean<n,k,p,T>::update_variance"); + trace::exiting("mln::clustering::k_mean::update_variance"); return result; - } - + } + //-------------------------------------------------------------------------- // Main loop @@ -626,9 +742,8 @@ namespace mln template <typename I> inline void k_mean<n,k,p,T>::loop(const Image<I>& _input) - //void k_mean<n,k,p,T>::loop() { - trace::entering("mln::clustering::k_mean<n,k,p,T>::loop"); + trace::entering("mln::clustering::k_mean::loop"); T within_variance = INT_MAX-1; T old_variance = INT_MAX; @@ -658,12 +773,10 @@ namespace mln std::cout << i << " : " << within_variance << std::endl; } - trace::exiting("mln::clustering::k_mean<n,k,p,T>::loop"); - } - - + trace::exiting("mln::clustering::k_mean::loop"); + } -#endif // ! MLN_INCLUDE_ONLY +# endif // ! MLN_INCLUDE_ONLY } // end of namespace mln::clustering diff --git a/scribo/sandbox/green/mln/clustering/kmean1d.hh b/scribo/sandbox/green/mln/clustering/kmean1d.hh index c3c1add..57d246b 100644 --- a/scribo/sandbox/green/mln/clustering/kmean1d.hh +++ b/scribo/sandbox/green/mln/clustering/kmean1d.hh @@ -1,4 +1,4 @@ -// Copyright (C) 2008,2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -24,7 +24,7 @@ // executable file might be covered by the GNU General Public License. #ifndef MLN_CLUSTERING_KMEAN1D_HH -#define MLN_CLUSTERING_KMEAN1D_HH +# define MLN_CLUSTERING_KMEAN1D_HH /// \file /// @@ -34,36 +34,63 @@ /// the greylevel attribute inspite of the pixel attribute. The /// algorithm is independant from the image dimension. But, we have to /// compute one time the histogram. In fact, we move a recurrent cost -/// to a fix cost in the complexity. This version is very adapted to +/// by a fix cost in the complexity. This version is very adapted to /// images with small quantification. +/// +/// The following sample is a typical use of the new kmean implementation. +/// +/// #include <iostream> +/// #include <mln/clustering/kmean1d.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/pgm/load.hh> +/// #include <mln/value/int_u8.hh> +/// +/// int main() +/// { +/// typedef mln::value::int_u8 t_int_u8; +/// typedef mln::image2d<t_int_u8> t_image2d_int_u8; +/// t_image2d_int_u8 img_int_u8; +/// +/// mln::io::pgm::load(img_int_u8, OLENA_IMG_PATH"/house.pgm"); +/// mln::clustering::kmean1d<double, 8> kmean(img_int_u8, 3); +/// +/// kmean.launch_n_times(); +/// +/// return 0; +/// } + +# include <limits.h> +# include <iostream> -#include <limits.h> -#include <iostream> -#include <mln/trace/entering.hh> -#include <mln/trace/exiting.hh> +# include <mln/accu/stat/histo1d.hh> -#include <mln/core/contract.hh> -#include <mln/trait/value_.hh> -#include <mln/accu/stat/histo1d.hh> +# include <mln/core/contract.hh> +# include <mln/core/image/image2d.hh> +# include <mln/core/macros.hh> -#include <mln/math/min.hh> -#include <mln/math/sqr.hh> -#include <mln/norm/l2.hh> +# include <mln/data/compute.hh> +# include <mln/data/fill.hh> +# include <mln/debug/println.hh> -#include <mln/core/image/image2d.hh> -#include <mln/value/int_u.hh> -#include <mln/value/rgb8.hh> -#include <mln/core/macros.hh> +# include <mln/io/ppm/save.hh> +# include <mln/io/pgm/save.hh> -#include <mln/data/compute.hh> -#include <mln/debug/println.hh> -#include <mln/data/fill.hh> -#include <mln/literal/zero.hh> -#include <mln/labeling/colorize.hh> -#include <mln/labeling/mean_values.hh> +# include <mln/literal/zero.hh> +# include <mln/labeling/colorize.hh> +# include <mln/labeling/mean_values.hh> -#include <mln/io/ppm/save.hh> -#include <mln/io/pgm/save.hh> +# include <mln/math/min.hh> +# include <mln/math/sqr.hh> +# include <mln/norm/l2.hh> + +# include <mln/trace/entering.hh> +# include <mln/trace/exiting.hh> + +# include <mln/trait/value_.hh> + +# include <mln/value/int_u.hh> +# include <mln/value/rgb8.hh> namespace mln { @@ -85,8 +112,8 @@ namespace mln /// temporary images for computations and produces images as result. Images /// play the role of matrix or vector in standard statistic algoritm. /// - /// T is the type used for computations (float or double). - /// n is the quantification for the image grayscale. + /// Param T is the type used for computations (float or double). + /// Param n is the quantification for the image grayscale. template <typename T, unsigned n> struct kmean1d { @@ -117,32 +144,22 @@ namespace mln ///} - //------------------------------------------------------------------------ - // Constructor - //------------------------------------------------------------------------ - - /// \brief Constructor + /// \brief Constructor. /// \param[in] point : the image as the population of pixels. /// \param[in] k_center : the number of centers. /// \param[in] watch_dog : the limit to observe the convergence (10). /// \param[in] n_times : the number of times that we executed kmean(10). - kmean1d(const t_point_img& point, const unsigned k_center, const unsigned watch_dog = 10, const unsigned n_times = 10); - //------------------------------------------------------------------------ - // Accessors - //------------------------------------------------------------------------ - - /// Mutator and accessors. + /// Mutators and accessors. /// \{ /// \brief Mutator and accessors are required for debugging and testing. /// /// Testing needs to hack the kmean loop process in order to verify each /// step of the algorithm. - void set_point(t_point_img& point); void set_histo(t_histo_img& histo); void set_number(t_number_img& number); @@ -173,31 +190,21 @@ namespace mln /// \} - //------------------------------------------------------------------------ - // Initializations of centers - //------------------------------------------------------------------------ - /// Initialization of centers. /// \{ - /// \brief Two ways: Regular greylevel tick or random greylevel value or. + /// \brief Initialize centers by regular tick or random position. /// /// There is two way to proceed the initialization. First of all, we /// divide the greyscale in regular tick and we assigne them to the mean /// of the centers. Finaly, we can ask random initialization along the /// greyscale axis. The second process is needed to launch_n_times the /// kmean and converge to the best descent. - void init_mean(); void init_mean_regular(); void init_mean_random(); - /// \} - //------------------------------------------------------------------------ - // Computations of distance, group, center, within variance - //------------------------------------------------------------------------ - /// Computations of distance, group, center, within variance. /// \{ /// \brief Update the statistical information needed by the kmean process. @@ -206,36 +213,27 @@ namespace mln /// first compute. Then we assign the pixels to their nearest center. /// The new location of each center can then update. Finaly, hommogeneity /// in group is observed by the within variance. - void update_distance(); void update_group(); void update_mean(); void update_variance(); - /// \} - //------------------------------------------------------------------------ - // Main loop - //------------------------------------------------------------------------ /// kmean main loop /// \{ /// \brief User interface to launch the kmean process. /// - /// There are two ways to launch the kmean process. The first one allow to - /// run it one time until convergence. As the process is a descent, it - /// depends on the initial center locations. The second call allow us to - /// rerun the process many times and to keep the best result (the one - /// with the smallest within variance). - + /// There are two ways to launch the kmean process. The first + /// one allows to run it one time until convergence. As the + /// process is a descent, it depends on the initial center + /// location. The second call allow us to rerun the process + /// many times and to keep the best result (the one with the + /// smallest within variance). void launch_one_time(); void launch_n_times(); - /// \} - //------------------------------------------------------------------------ - // Checking the validiy of the results - //------------------------------------------------------------------------ /// Checking the validity of the results. /// \{ @@ -244,15 +242,10 @@ namespace mln /// After each launching the kmean process one time, we need to know if /// the descent was successfull or not. The method is_valid_descent do it /// for us. The method looks for a bad center (a class with no greylevel - /// associate to it) and a failure in the convergence. - + /// associate to it) or a failure in the convergence. bool is_descent_valid(); - /// \} - //------------------------------------------------------------------------ - // Debugging tools - //------------------------------------------------------------------------ /// Debugging tools /// \{ @@ -264,12 +257,10 @@ namespace mln /// greylevel image. The update_cnv and finalize_cnv methods are used to /// trace the convergence. They fill two images with the mean info and /// the within variance info along the convergence and the tries. - void build_label_dbg(); void build_all_dbg(); void update_cnv(); void finalize_cnv(); - /// \} @@ -279,20 +270,19 @@ namespace mln /// \brief These parameters control the convergence of the process. /// /// The first parameter, k_center, defines the number of center for kmean. - /// In fact, watch_dog limit the number of iteration that a simple kmean + /// In fact, watch_dog limits the number of iteration that a simple kmean /// loop can do. If the process reaches the watch_dog limit, it means /// that the process will not converge at all. The second parameter /// n_times is the number of times we launch again and again the simple /// kmean loop. As the kmean process is a descent, restarting the process /// from different location will confort us in that we found a global /// minima, not just a local one. - unsigned _k_center; unsigned _watch_dog; unsigned _n_times; - /// \} + /// Convergence information. /// \{ /// \brief This information is used to follow the convergence. @@ -300,26 +290,25 @@ namespace mln /// The within_variance is the homogeniety indicator for the /// kmean process. The ideal situation is to find the center /// with the minimum variance around them. The within variance - /// is just the sum of all variance around the centers. The - /// current_step variable allows us to remember the current - /// iteration in the kmean loop. This information is needed by - /// is_descent_valid routine which decide if convergence occurs - /// or not. The current_step info explicit where we are in the - /// kmean iteration. The last information, current_launching, - /// traces the progress while iterates kmean loop again and - /// again. The flag is_number_valid is set when a empty class - /// appears. This flag inhibit the process and force to restart - /// a try. - + /// is just the sum of all ponderated variances around the + /// centers. The current_step variable allows us to remember the + /// current iteration in the kmean loop. This information is + /// needed by is_descent_valid routine which decide if + /// convergence occurs or not. The current_step info explicit + /// where we are in the kmean iteration. The last information, + /// current_launching, traces the progress while iterates kmean + /// loop again and again. The flag is_number_valid is set when a + /// empty class appears. This flag inhibit the process and + /// force to restart a try. t_result _within_variance; unsigned _current_step; unsigned _current_launching; bool _is_number_valid; static const unsigned _N_TRIES = 3; - /// \} + /// Result of the kmean process. /// \{ /// \brief The center location are the major results of the kmean process. @@ -327,23 +316,23 @@ namespace mln /// The kmean process result is composed by three information. The best /// launching iteration, the best within variance obtained and the /// location of the centers associated. - unsigned _launching_min; t_result _variance_min; t_mean_img _mean_min; + /// \} + /// Inputs. /// \{ - /// \brief The inputs are the distribution of the values and the values. + /// \brief The inputs are the distribution of the values. /// /// The point image is a saving data for the real inputs. In fact, we use /// the histogram information in the optimized kmean process. - t_point_img _point; t_histo_img _histo; - ///\} + /// Centers description. /// \{ /// \brief Centers are described by the first moment of their group. @@ -354,13 +343,12 @@ namespace mln /// convergence. The number of pixels is used as integrity indicator. /// A center with no point is a non sense. Theses informations are updated /// after each kmean iteration. - t_number_img _number; // k x 1 t_mean_img _mean; // k x 1 t_variance_img _variance; // k x 1 within group - /// \} + /// Greylevels description. /// \{ /// \brief The information are concerned with the greylevel input image. @@ -369,15 +357,14 @@ namespace mln /// which pixel) is assigned to a center. The distance image give us a /// clue on how a greylevel could contribute to a center. The summation /// over the greylevels of a center give us the within variance. - t_group_img _group; // g x 1 because dim(t_value) = 1 t_distance_img _distance; // label x graylevel - /// \} + /// Debugging, calibrating and testing results. /// \{ - /// \brief Some exports information to control the results. + /// \brief Some information exports to control the results. /// /// We come back in the input space. Label is the image which associates /// each pixel to its center. Color is the image which gives a random rgb @@ -385,14 +372,13 @@ namespace mln /// label (assigned to the same center) in the image. The mean image /// associate the mean of each pixel center to each pixel. We obtain thus /// a rebuilded image. - t_label_dbg _label_dbg; t_color_dbg _color_dbg; t_mean_dbg _mean_dbg; - /// \} - /// Debugging, calibrating and testing convergence. + + /// Variance and center evolutions. /// \{ /// \brief Trace the variance and the center evolution. /// @@ -401,10 +387,8 @@ namespace mln /// kmean loop or along the different launch. The variance_cnv is a trace /// of the within variance along the kmean loop or along the different /// launch. - t_mean_cnv _mean_cnv; t_variance_cnv _variance_cnv; - /// \} }; @@ -803,8 +787,6 @@ namespace mln { trace::entering("mln::clustering::kmean1d::update_mean"); - /// FIXME VERIFIER QUE L'ON PEUT OBTENIR UNE IMAGE EN NDG SIGNE - // avec g le niveau de gris (signed or not signed) // w[g] la classe de g sous forme d'image // h[g] l'histogramme de l'image sous forme d'image @@ -872,7 +854,7 @@ namespace mln for_all(g) { - if (l.ind() == _group(g)) + if (static_cast<t_label>(l.ind()) == _group(g)) _variance(l) += _distance(point2d(g.ind(), l.ind())); } @@ -1091,7 +1073,7 @@ namespace mln trace::exiting("mln::clustering::kmean1d::launch_n_times"); } -#endif // ! MLN_INCLUDE_ONLY +# endif // ! MLN_INCLUDE_ONLY } // end of namespace mln::clustering diff --git a/scribo/sandbox/green/mln/clustering/kmean2d.hh b/scribo/sandbox/green/mln/clustering/kmean2d.hh index e4918db..51aaf49 100644 --- a/scribo/sandbox/green/mln/clustering/kmean2d.hh +++ b/scribo/sandbox/green/mln/clustering/kmean2d.hh @@ -1,4 +1,4 @@ -// Copyright (C) 2008,2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -24,52 +24,102 @@ // executable file might be covered by the GNU General Public License. #ifndef MLN_CLUSTERING_KMEAN2D_HH -#define MLN_CLUSTERING_KMEAN2D_HH +# define MLN_CLUSTERING_KMEAN2D_HH /// \file /// -/// \brief Implements the optimized kmean algorithm. +/// \brief Implements the optimized kmean algorithm in 2d. /// /// This algorithm is optimized in the way it proceeds directly with /// the greylevel attribute inspite of the pixel attribute. The /// algorithm is independant from the image dimension. But, we have to /// compute one time the histogram. In fact, we move a recurrent cost -/// to a fix cost in the complexity. This version is very adapted to -/// images with small quantification. - -#include <limits.h> -#include <iostream> -#include <mln/trace/entering.hh> -#include <mln/trace/exiting.hh> - -#include <mln/core/contract.hh> -#include <mln/trait/value_.hh> -#include <mln/accu/stat/histo2d.hh> - -#include <mln/math/min.hh> -#include <mln/math/sqr.hh> -#include <mln/norm/l2.hh> - -#include <mln/core/image/image2d.hh> -#include <mln/core/concept/image.hh> -#include <mln/value/int_u.hh> -#include <mln/value/rgb8.hh> -#include <mln/value/rg.hh> -#include <mln/core/macros.hh> - -#include <mln/data/compute.hh> -#include <mln/debug/println.hh> -#include <mln/data/fill.hh> -#include <mln/literal/zero.hh> -#include <mln/literal/one.hh> -#include <mln/labeling/colorize.hh> -#include <mln/labeling/mean_values.hh> - -#include <mln/io/ppm/save.hh> -#include <mln/io/pgm/save.hh> - -#include <mln/util/array.hh> -#include <mln/algebra/vec.hh> +/// by a fix cost in the complexity. This version is very adapted to +/// images with small quantification. But, in 2d, the execution +/// becomes very slow. It's just normal because the quantification is +/// 8 bits per axis. So the actual histogram is bigger than the image. +/// +/// Take care to the following point: The within variance is still a +/// scalar value because we take the distance between two points and +/// the result is a scalar from the geometrical point of view. An +/// alternative implementation could study the variance/covariance +/// matrix of each sub data clouds and works with the trace of the +/// within variance matrix (as we do for the fisher criteria in N-d). +/// +/// The following sample is a typical use of the new kmean implementation. +/// +/// #include <iostream> +/// #include <mln/clustering/kmean2d.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/data/transform.hh> +/// #include <mln/fun/v2v/rgb_to_rg.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/ppm/load.hh> +/// #include <mln/value/rg.hh> +/// #include <mln/value/rgb8.hh> +/// +/// int main() +/// { +/// typedef mln::value::rg<8> t_rg8; +/// typedef mln::value::rgb8 t_rgb8; +/// typedef mln::image2d<t_rgb8> t_image2d_rgb8; +/// typedef mln::image2d<t_rg8> t_image2d_rg8; +/// typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; +/// +/// t_image2d_rgb8 img_rgb8; +/// t_image2d_rg8 img_rg8; +/// +/// mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); +/// +/// img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); +/// +/// mln::clustering::kmean2d<double, 8> kmean(img_rg8, 3); +/// +/// kmean.launch_n_times(); +/// +/// return 0; +/// } + +# include <limits.h> +# include <iostream> + +# include <mln/accu/stat/histo2d.hh> + +# include <mln/algebra/vec.hh> + +# include <mln/core/concept/image.hh> +# include <mln/core/contract.hh> +# include <mln/core/image/image2d.hh> +# include <mln/core/macros.hh> + +# include <mln/data/compute.hh> +# include <mln/data/fill.hh> + +# include <mln/debug/println.hh> + +# include <mln/io/pgm/save.hh> +# include <mln/io/ppm/save.hh> + +# include <mln/labeling/colorize.hh> +# include <mln/labeling/mean_values.hh> + +# include <mln/literal/one.hh> +# include <mln/literal/zero.hh> + +# include <mln/math/min.hh> +# include <mln/math/sqr.hh> +# include <mln/norm/l2.hh> + +# include <mln/trace/entering.hh> +# include <mln/trace/exiting.hh> + +# include <mln/trait/value_.hh> + +# include <mln/util/array.hh> + +# include <mln/value/int_u.hh> +# include <mln/value/rgb8.hh> +# include <mln/value/rg.hh> namespace mln { @@ -85,21 +135,22 @@ namespace mln namespace clustering { + /// \brief Implements the kmean algorithm in a specific way. /// - /// This version of the kmean algorithm uses a greyscale image as input, - /// temporary images for computations and produces images as result. Images - /// play the role of matrix or vector in standard statistic algoritm. + /// This version of the kmean algorithm uses a 2-channels + /// (red/green channel) image as input, temporary images for + /// computations and produces images as result. Images play the + /// role of matrix or vector in standard statistic algoritm. /// - /// T is the type used for computations (float or double). - /// n is the quantification for the image grayscale. + /// Param T is the type used for computations (float or double). + /// Param n is the quantification for the image grayscale. template <typename T, unsigned n> struct kmean2d { /// Type definitions. /// \brief A few type definitions to limit the refactoring impact. ///{ - typedef value::rgb<8> t_rgb; typedef value::label<8> t_label; typedef value::rg<n> t_value; @@ -108,7 +159,7 @@ namespace mln typedef T t_result1d; typedef algebra::vec<2,T> t_result2d; - /// FIXME t_point n'est pas forcément une image 2d, bien au contraire. + /// \fixme t_point_img is not an image2d ... but it works like this ... typedef image2d<t_value> t_point_img; typedef image2d<unsigned> t_histo_img; typedef util::array<t_result1d> t_number_img; @@ -128,35 +179,24 @@ namespace mln typedef util::array<t_mean_set> t_mean_cnv; typedef image1d<t_result1d> t_variance_val; typedef util::array<t_variance_val> t_variance_cnv; - ///} - //------------------------------------------------------------------------ - // Constructor - //------------------------------------------------------------------------ - - /// \brief Constructor + /// \brief Constructor. /// \param[in] point : the image as the population of pixels. /// \param[in] k_center : the number of centers. /// \param[in] watch_dog : the limit to observe the convergence (10). /// \param[in] n_times : the number of times that we executed kmean(10). - kmean2d(const t_point_img& point, const unsigned k_center, const unsigned watch_dog = 10, const unsigned n_times = 10); - //------------------------------------------------------------------------ - // Accessors - //------------------------------------------------------------------------ - - /// Mutator and accessors. + /// Mutators and accessors. /// \{ /// \brief Mutator and accessors are required for debugging and testing. /// /// Testing needs to hack the kmean loop process in order to verify each /// step of the algorithm. - void set_point(t_point_img& point); void set_histo(t_histo_img& histo); void set_number(t_number_img& number); @@ -187,10 +227,10 @@ namespace mln /// \} - //------------------------------------------------------------------------ - // Printing temporary results - //------------------------------------------------------------------------ - + /// Show temporary results. + /// \{ + /// \brief Formating temporary outputs for debugging. + /// void print_mean(); void print_number(); void print_variance(); @@ -198,32 +238,18 @@ namespace mln void print_group(); void print_point(); void print_histo(); - - //------------------------------------------------------------------------ - // Initializations of centers - //------------------------------------------------------------------------ + /// \} /// Initialization of centers. /// \{ - /// \brief Two ways: Regular greylevel tick or random greylevel value or. + /// \brief Initialize centers by random position. /// - /// There is two way to proceed the initialization. First of all, we - /// divide the greyscale in regular tick and we assigne them to the mean - /// of the centers. Finaly, we can ask random initialization along the - /// greyscale axis. The second process is needed to launch_n_times the - /// kmean and converge to the best descent. - + /// Assign a random position in the 2-channel space to each center. void init_mean(); - void init_mean_regular(); void init_mean_random(); - /// \} - //------------------------------------------------------------------------ - // Computations of distance, group, center, within variance - //------------------------------------------------------------------------ - /// Computations of distance, group, center, within variance. /// \{ /// \brief Update the statistical information needed by the kmean process. @@ -232,36 +258,27 @@ namespace mln /// first compute. Then we assign the pixels to their nearest center. /// The new location of each center can then update. Finaly, hommogeneity /// in group is observed by the within variance. - void update_distance(); void update_group(); void update_mean(); void update_variance(); - /// \} - //------------------------------------------------------------------------ - // Main loop - //------------------------------------------------------------------------ /// kmean main loop /// \{ /// \brief User interface to launch the kmean process. /// - /// There are two ways to launch the kmean process. The first one allow to - /// run it one time until convergence. As the process is a descent, it - /// depends on the initial center locations. The second call allow us to - /// rerun the process many times and to keep the best result (the one - /// with the smallest within variance). - + /// There are two ways to launch the kmean process. The first + /// one allows to run it one time until convergence. As the + /// process is a descent, it depends on the initial center + /// location. The second call allow us to rerun the process + /// many times and to keep the best result (the one with the + /// smallest within variance). void launch_one_time(); void launch_n_times(); - /// \} - //------------------------------------------------------------------------ - // Checking the validiy of the results - //------------------------------------------------------------------------ /// Checking the validity of the results. /// \{ @@ -269,16 +286,11 @@ namespace mln /// /// After each launching the kmean process one time, we need to know if /// the descent was successfull or not. The method is_valid_descent do it - /// for us. The method looks for a bad center (a class with no greylevel - /// associate to it) and a failure in the convergence. - + /// for us. The method looks for a bad center (a class with no r/g color + /// associate to it) or a failure in the convergence. bool is_descent_valid(); - /// \} - //------------------------------------------------------------------------ - // Debugging tools - //------------------------------------------------------------------------ /// Debugging tools /// \{ @@ -290,13 +302,11 @@ namespace mln /// greylevel image. The update_cnv and finalize_cnv methods are used to /// trace the convergence. They fill two images with the mean info and /// the within variance info along the convergence and the tries. - void build_label_dbg(); void build_mean_dbg(); void build_all_dbg(); void update_cnv(); void finalize_cnv(); - /// \} @@ -313,13 +323,12 @@ namespace mln /// kmean loop. As the kmean process is a descent, restarting the process /// from different location will confort us in that we found a global /// minima, not just a local one. - unsigned _k_center; unsigned _watch_dog; unsigned _n_times; - /// \} + /// Convergence information. /// \{ /// \brief This information is used to follow the convergence. @@ -337,16 +346,15 @@ namespace mln /// again. The flag is_number_valid is set when a empty class /// appears. This flag inhibit the process and force to restart /// a try. - t_result1d _within_variance; unsigned _current_step; unsigned _current_launching; bool _is_number_valid; static const unsigned _N_TRIES = 3; - /// \} + /// Result of the kmean process. /// \{ /// \brief The center location are the major results of the kmean process. @@ -354,10 +362,11 @@ namespace mln /// The kmean process result is composed by three information. The best /// launching iteration, the best within variance obtained and the /// location of the centers associated. - unsigned _launching_min; t_result1d _variance_min; t_mean_img _mean_min; + /// \} + /// Inputs. /// \{ @@ -365,12 +374,11 @@ namespace mln /// /// The point image is a saving data for the real inputs. In fact, we use /// the histogram information in the optimized kmean process. - t_point_img _point; t_histo_img _histo; - ///\} + /// Centers description. /// \{ /// \brief Centers are described by the first moment of their group. @@ -381,27 +389,25 @@ namespace mln /// convergence. The number of pixels is used as integrity indicator. /// A center with no point is a non sense. Theses informations are updated /// after each kmean iteration. - t_number_img _number; // k x 1 - t_mean_img _mean; // k x 1 + t_mean_img _mean; // k x 2 t_variance_img _variance; // k x 1 within group - /// \} + /// Greylevels description. /// \{ - /// \brief The information are concerned with the greylevel input image. + /// \brief The information are concerned with the 2-channel input image. /// - /// The group image allow us to decide which greylevel (and of course + /// The group image allow us to decide which r/g value (and of course /// which pixel) is assigned to a center. The distance image give us a - /// clue on how a greylevel could contribute to a center. The summation - /// over the greylevels of a center give us the within variance. - - t_group_img _group; // g x 1 because dim(t_value) = 1 - t_distance_img _distance; // label x graylevel - + /// clue on how a r/g value could contribute to a center. The summation + /// over the r/g values of a center give us the within variance. + t_group_img _group; // g x 2 because dim(t_value) = 2 + t_distance_img _distance; // label x r/g value /// \} + /// Debugging, calibrating and testing results. /// \{ /// \brief Some exports information to control the results. @@ -412,14 +418,13 @@ namespace mln /// label (assigned to the same center) in the image. The mean image /// associate the mean of each pixel center to each pixel. We obtain thus /// a rebuilded image. - t_label_dbg _label_dbg; t_color_dbg _color_dbg; t_mean_dbg _mean_dbg; - /// \} - /// Debugging, calibrating and testing convergence. + + /// Variance and center evolutions. /// \{ /// \brief Trace the variance and the center evolution. /// @@ -428,10 +433,8 @@ namespace mln /// kmean loop or along the different launch. The variance_cnv is a trace /// of the within variance along the kmean loop or along the different /// launch. - t_mean_cnv _mean_cnv; t_variance_cnv _variance_cnv; - /// \} }; @@ -448,7 +451,7 @@ namespace mln const unsigned watch_dog, const unsigned n_times) { - trace::entering("mln::clustering::kmean2d::kmean2d"); + trace::entering("mln::clustering::kmean2d::cstor"); mln_precondition(point.is_valid()); _k_center = k_center; @@ -516,7 +519,7 @@ namespace mln _mean_cnv.append(mean_set); } - trace::exiting("mln::clustering::kmean2d::kmean2d"); + trace::exiting("mln::clustering::kmean2d::cstor"); } //-------------------------------------------------------------------------- @@ -796,15 +799,15 @@ namespace mln { trace::entering("mln::clustering::kmean2d::print_histo"); - mln_piter(t_histo_img) rgb(_histo.domain()); + mln_piter(t_histo_img) rg(_histo.domain()); - for_all(rgb) + for_all(rg) { - if (0 < _histo(rgb)) + if (0 < _histo(rg)) { - std::cout << "histo(r=" << rgb.row(); - std::cout << ", g=" << rgb.col(); - std::cout << ")= " << _histo(rgb); + std::cout << "histo(r=" << rg.row(); + std::cout << ", g=" << rg.col(); + std::cout << ")= " << _histo(rg); std::cout << std::endl; } } @@ -818,15 +821,15 @@ namespace mln { trace::entering("mln::clustering::kmean2d::print_group"); - mln_piter(t_group_img) rgb(_group.domain()); + mln_piter(t_group_img) rg(_group.domain()); - for_all(rgb) + for_all(rg) { - if (0 < _histo(rgb)) + if (0 < _histo(rg)) { - std::cout << "group(r=" << rgb.row(); - std::cout << ", g=" << rgb.col(); - std::cout << ")= " << _group(rgb); + std::cout << "group(r=" << rg.row(); + std::cout << ", g=" << rg.col(); + std::cout << ")= " << _group(rg); std::cout << std::endl; } } @@ -844,16 +847,16 @@ namespace mln for_all(l) { - mln_piter(t_distance_val) rgb(_distance[l.index_()].domain()); + mln_piter(t_distance_val) rg(_distance[l.index_()].domain()); - for_all(rgb) + for_all(rg) { - if (0 < _histo(rgb)) + if (0 < _histo(rg)) { std::cout << "distance(l=" << l.index_(); - std::cout << ",r=" << rgb.row(); - std::cout << ", g=" << rgb.col(); - std::cout << ")= " << _distance[l.index_()](rgb); + std::cout << ",r=" << rg.row(); + std::cout << ", g=" << rg.col(); + std::cout << ")= " << _distance[l.index_()](rg); std::cout << std::endl; } } @@ -960,9 +963,9 @@ namespace mln { trace::entering("mln::clustering::kmean2d::update_group"); - mln_piter(t_group_img) rgb(_group.domain()); + mln_piter(t_group_img) rg(_group.domain()); - for_all(rgb) + for_all(rg) { mln_eiter(t_distance_img) l(_distance); t_result1d min = mln_max(t_result1d); @@ -972,9 +975,9 @@ namespace mln for_all(l) { - if (min > _distance[l.index_()](rgb)) + if (min > _distance[l.index_()](rg)) { - min = _distance[l.index_()](rgb); + min = _distance[l.index_()](rg); label = l.index_(); } @@ -984,7 +987,7 @@ namespace mln //std::cout << "g = " << g << std::endl; - _group(rgb) = label; + _group(rg) = label; //std::cout << "group = " << _group(g) << std::endl; //std::cout << "-----------" << std::endl; } @@ -998,8 +1001,6 @@ namespace mln { trace::entering("mln::clustering::kmean2d::update_mean"); - /// FIXME VERIFIER QUE L'ON PEUT OBTENIR UNE IMAGE EN NDG SIGNE - // avec g le niveau de gris (signed or not signed) // w[g] la classe de g sous forme d'image // h[g] l'histogramme de l'image sous forme d'image @@ -1021,14 +1022,14 @@ namespace mln _mean[em.index_()] = literal::zero; } - mln_piter(t_group_img) rgb(_group.domain()); + mln_piter(t_group_img) rg(_group.domain()); - for_all(rgb) + for_all(rg) { // peut être faut-il le decomposer par composantes - _mean[_group(rgb)][0] += rgb.row() * _histo(rgb); - _mean[_group(rgb)][1] += rgb.col() * _histo(rgb); - _number(_group(rgb)) += _histo(rgb); + _mean[_group(rg)][0] += rg.row() * _histo(rg); + _mean[_group(rg)][1] += rg.col() * _histo(rg); + _number(_group(rg)) += _histo(rg); } mln_eiter(t_mean_img) l(_mean); @@ -1072,12 +1073,12 @@ namespace mln { _variance[l.index_()] = literal::zero; - mln_piter(t_group_img) rgb(_group.domain()); + mln_piter(t_group_img) rg(_group.domain()); - for_all(rgb) + for_all(rg) { - if (l.index_() == _group(rgb)) - _variance[l.index_()] += _distance[l.index_()](rgb); + if (l.index_() == _group(rg)) + _variance[l.index_()] += _distance[l.index_()](rg); } _within_variance += _variance[l.index_()]; @@ -1334,7 +1335,7 @@ namespace mln trace::exiting("mln::clustering::kmean2d::launch_n_times"); } -#endif // ! MLN_INCLUDE_ONLY +# endif // ! MLN_INCLUDE_ONLY } // end of namespace mln::clustering diff --git a/scribo/sandbox/green/mln/clustering/kmean3d.hh b/scribo/sandbox/green/mln/clustering/kmean3d.hh index fb1a8df..c35d2a7 100644 --- a/scribo/sandbox/green/mln/clustering/kmean3d.hh +++ b/scribo/sandbox/green/mln/clustering/kmean3d.hh @@ -1,4 +1,4 @@ -// Copyright (C) 2008,2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -24,51 +24,102 @@ // executable file might be covered by the GNU General Public License. #ifndef MLN_CLUSTERING_KMEAN3D_HH -#define MLN_CLUSTERING_KMEAN3D_HH +# define MLN_CLUSTERING_KMEAN3D_HH /// \file /// -/// \brief Implements the optimized kmean algorithm. +/// \brief Implements the optimized kmean algorithm in 3d. /// /// This algorithm is optimized in the way it proceeds directly with -/// the rgb values inspite of the pixel attribute. The +/// the rgb value inspite of the pixel attribute. The /// algorithm is independant from the image dimension. But, we have to /// compute one time the histogram. In fact, we move a recurrent cost -/// to a fix cost in the complexity. This version is adapted to -/// image with small quantification. - -#include <limits.h> -#include <iostream> -#include <mln/trace/entering.hh> -#include <mln/trace/exiting.hh> - -#include <mln/core/contract.hh> -#include <mln/trait/value_.hh> -#include <mln/accu/stat/histo3d_rgb.hh> - -#include <mln/math/min.hh> -#include <mln/math/sqr.hh> -#include <mln/norm/l2.hh> - -#include <mln/core/image/image2d.hh> -#include <mln/core/concept/image.hh> -#include <mln/value/int_u.hh> -#include <mln/value/rgb8.hh> -#include <mln/core/macros.hh> - -#include <mln/data/compute.hh> -#include <mln/debug/println.hh> -#include <mln/data/fill.hh> -#include <mln/literal/zero.hh> -#include <mln/literal/one.hh> -#include <mln/labeling/colorize.hh> -#include <mln/labeling/mean_values.hh> - -#include <mln/io/ppm/save.hh> -#include <mln/io/pgm/save.hh> - -#include <mln/util/array.hh> -#include <mln/algebra/vec.hh> +/// by a fix cost in the complexity. This version is very adapted to +/// images with small quantification. But, in 3d, the execution +/// becomes very slow. It's just normal because the quantification is +/// n bits per axis. So the actual histogram may be bigger than the image. +/// +/// Take care to the following point: The within variance is still a +/// scalar value because we take the distance between two points and +/// the result is a scalar from the geometrical point of view. An +/// alternative implementation could study the variance/covariance +/// matrix of each sub data clouds and works with the trace of the +/// within variance matrix (as we do for the fisher criteria in N-d). +/// +/// The following sample is a typical use of the new kmean implementation. +/// +/// #include <iostream> +/// #include <mln/clustering/kmean3d.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/data/transform.hh> +/// #include <mln/fun/v2v/rgb8_to_rgbn.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/ppm/load.hh> +/// #include <mln/value/rgb.hh> +/// #include <mln/value/rgb8.hh> +/// +/// int main() +/// { +/// typedef mln::clustering::kmean3d<double,5> t_kmean; +/// typedef mln::value::rgb8 t_rgb8; +/// typedef mln::value::rgb<5> t_rgb5; +/// typedef mln::image2d<t_rgb8> t_image2d_rgb8; +/// typedef mln::image2d<t_rgb5> t_image2d_rgb5; +/// typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; +/// +/// t_image2d_rgb8 img_rgb8; +/// t_image2d_rgb5 img_rgb5; +/// +/// mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); +/// +/// img_rgb5=mln::data::transform(img_rgb8, t_rgb8_to_rgb5()); +/// +/// t_kmean kmean(img_rgb5, 3); +/// +/// kmean.launch_n_times(); +/// +/// return 0; +/// } + +# include <limits.h> +# include <iostream> + +# include <mln/accu/stat/histo3d_rgb.hh> + +# include <mln/algebra/vec.hh> + +# include <mln/math/min.hh> +# include <mln/math/sqr.hh> +# include <mln/norm/l2.hh> + +# include <mln/core/concept/image.hh> +# include <mln/core/contract.hh> +# include <mln/core/image/image2d.hh> +# include <mln/core/macros.hh> + +# include <mln/data/compute.hh> +# include <mln/data/fill.hh> +# include <mln/debug/println.hh> + +# include <mln/labeling/colorize.hh> +# include <mln/labeling/mean_values.hh> + +# include <mln/literal/one.hh> +# include <mln/literal/zero.hh> + +# include <mln/io/ppm/save.hh> +# include <mln/io/pgm/save.hh> + +# include <mln/trace/entering.hh> +# include <mln/trace/exiting.hh> + +# include <mln/trait/value_.hh> + +# include <mln/util/array.hh> + +# include <mln/value/int_u.hh> +# include <mln/value/rgb8.hh> + namespace mln { @@ -98,7 +149,6 @@ namespace mln /// Type definitions. /// \brief A few type definitions to limit the refactoring impact. ///{ - typedef value::rgb<8> t_rgb; typedef value::label<8> t_label; typedef value::rgb<n> t_value; @@ -108,7 +158,7 @@ namespace mln typedef T t_result1d; typedef algebra::vec<3,T> t_result3d; - /// FIXME t_point n'est pas forcément une image 2d, bien au contraire. + /// \fixme t_point is not an image2d, it may be something else ... typedef image2d<t_value> t_point_img; typedef image3d<unsigned> t_histo_img; typedef util::array<t_result1d> t_number_img; @@ -128,35 +178,26 @@ namespace mln typedef util::array<t_mean_set> t_mean_cnv; typedef image1d<t_result1d> t_variance_val; typedef util::array<t_variance_val> t_variance_cnv; - ///} - //------------------------------------------------------------------------ - // Constructor - //------------------------------------------------------------------------ - /// \brief Constructor + /// \brief Constructor. + /// /// \param[in] point : the image as the population of pixels. /// \param[in] k_center : the number of centers. /// \param[in] watch_dog : the limit to observe the convergence (10). /// \param[in] n_times : the number of times that we executed kmean(10). - kmean3d(const t_point_img& point, const unsigned k_center, const unsigned watch_dog = 10, const unsigned n_times = 10); - //------------------------------------------------------------------------ - // Accessors - //------------------------------------------------------------------------ - - /// Mutator and accessors. + /// Mutators and accessors. /// \{ /// \brief Mutator and accessors are required for debugging and testing. /// /// Testing needs to hack the kmean loop process in order to verify each /// step of the algorithm. - void set_point(t_point_img& point); void set_histo(t_histo_img& histo); void set_number(t_number_img& number); @@ -184,13 +225,13 @@ namespace mln // Normal outputs t_mean_img& to_result(); - /// \} - //------------------------------------------------------------------------ - // Printing temporary results - //------------------------------------------------------------------------ + /// Show temporary results. + /// \{ + /// \brief Formating temporary outputs for debugging. + /// void print_mean(); void print_number(); void print_variance(); @@ -198,32 +239,19 @@ namespace mln void print_group(); void print_point(); void print_histo(); + /// \} - //------------------------------------------------------------------------ - // Initializations of centers - //------------------------------------------------------------------------ /// Initialization of centers. /// \{ - /// \brief Two ways: Regular greylevel tick or random greylevel value or. + /// \brief Initialize centers by random position. /// - /// There is two way to proceed the initialization. First of all, we - /// divide the rgb space in regular tick and we assigne them to the mean - /// of the centers. Finaly, we can ask random initialization along the - /// greyscale axis. The second process is needed to launch_n_times the - /// kmean and converge to the best descent. - + /// Assign a random position in the rgb space to each center. void init_mean(); - void init_mean_regular(); void init_mean_random(); - /// \} - //------------------------------------------------------------------------ - // Computations of distance, group, center, within variance - //------------------------------------------------------------------------ - /// Computations of distance, group, center, within variance. /// \{ /// \brief Update the statistical information needed by the kmean process. @@ -232,36 +260,27 @@ namespace mln /// first compute. Then we assign the pixels to their nearest center. /// The new location of each center can then update. Finaly, hommogeneity /// in group is observed by the within variance. - void update_distance(); void update_group(); void update_mean(); void update_variance(); - /// \} - //------------------------------------------------------------------------ - // Main loop - //------------------------------------------------------------------------ /// kmean main loop /// \{ /// \brief User interface to launch the kmean process. /// - /// There are two ways to launch the kmean process. The first one allow to - /// run it one time until convergence. As the process is a descent, it - /// depends on the initial center locations. The second call allow us to - /// rerun the process many times and to keep the best result (the one - /// with the smallest within variance). - + /// There are two ways to launch the kmean process. The first + /// one allows to run it one time until convergence. As the + /// process is a descent, it depends on the initial center + /// location. The second call allow us to rerun the process + /// many times and to keep the best result (the one with the + /// smallest within variance). void launch_one_time(); void launch_n_times(); - /// \} - //------------------------------------------------------------------------ - // Checking the validiy of the results - //------------------------------------------------------------------------ /// Checking the validity of the results. /// \{ @@ -271,32 +290,22 @@ namespace mln /// the descent was successfull or not. The method is_valid_descent do it /// for us. The method looks for a bad center (a class with no greylevel /// associate to it) and a failure in the convergence. - bool is_descent_valid(); - /// \} - //------------------------------------------------------------------------ - // Debugging tools - //------------------------------------------------------------------------ - - /// Debugging tools + /// Checking the validity of the results. /// \{ - /// \brief These methods help to interpret results. + /// \brief These methods help us to determine the validity of the results. /// - /// The methods build_label_dbg and build_all_dbg work in the input data - /// space. The first one build the 2d image of labels. The second call the - /// first one and then builds the colorize label' image and the mean - /// greylevel image. The update_cnv and finalize_cnv methods are used to - /// trace the convergence. They fill two images with the mean info and - /// the within variance info along the convergence and the tries. - + /// After each launching the kmean process one time, we need to know if + /// the descent was successfull or not. The method is_valid_descent do it + /// for us. The method looks for a bad center (a class with no color + /// associate to it) or a failure in the convergence. void build_label_dbg(); void build_mean_dbg(); void build_all_dbg(); void update_cnv(); void finalize_cnv(); - /// \} @@ -313,13 +322,12 @@ namespace mln /// kmean loop. As the kmean process is a descent, restarting the process /// from different location will confort us in that we found a global /// minima, not just a local one. - unsigned _k_center; unsigned _watch_dog; unsigned _n_times; - /// \} + /// Convergence information. /// \{ /// \brief This information is used to follow the convergence. @@ -337,14 +345,12 @@ namespace mln /// again. The flag is_number_valid is set when a empty class /// appears. This flag inhibit the process and force to restart /// a try. - t_result1d _within_variance; unsigned _current_step; unsigned _current_launching; bool _is_number_valid; static const unsigned _N_TRIES = 3; - /// \} /// Result of the kmean process. @@ -354,10 +360,10 @@ namespace mln /// The kmean process result is composed by three information. The best /// launching iteration, the best within variance obtained and the /// location of the centers associated. - unsigned _launching_min; t_result1d _variance_min; t_mean_img _mean_min; + /// \} /// Inputs. /// \{ @@ -365,12 +371,11 @@ namespace mln /// /// The point image is a saving data for the real inputs. In fact, we use /// the histogram information in the optimized kmean process. - t_point_img _point; t_histo_img _histo; - ///\} + /// Centers description. /// \{ /// \brief Centers are described by the first moment of their group. @@ -381,11 +386,9 @@ namespace mln /// convergence. The number of pixels is used as integrity indicator. /// A center with no point is a non sense. Theses informations are updated /// after each kmean iteration. - t_number_img _number; // k x 1 - t_mean_img _mean; // k x 1 + t_mean_img _mean; // k x 3 t_variance_img _variance; // k x 1 within group - /// \} /// rgb image description. @@ -396,12 +399,11 @@ namespace mln /// which pixel) is assigned to a center. The distance image give us a /// clue on how a greylevel could contribute to a center. The summation /// over the rgb space of a center give us the within variance. - t_group_img _group; // g x 3 because dim(t_value) = 3 t_distance_img _distance; // label x rgb space - /// \} + /// Debugging, calibrating and testing results. /// \{ /// \brief Some exports information to control the results. @@ -412,14 +414,12 @@ namespace mln /// label (assigned to the same center) in the image. The mean image /// associate the mean of each pixel center to each pixel. We obtain thus /// a rebuilded image. - t_label_dbg _label_dbg; t_color_dbg _color_dbg; t_mean_dbg _mean_dbg; - /// \} - /// Debugging, calibrating and testing convergence. + /// Variance and center evolutions. /// \{ /// \brief Trace the variance and the center evolution. /// @@ -428,10 +428,8 @@ namespace mln /// kmean loop or along the different launch. The variance_cnv is a trace /// of the within variance along the kmean loop or along the different /// launch. - t_mean_cnv _mean_cnv; t_variance_cnv _variance_cnv; - /// \} }; @@ -448,7 +446,7 @@ namespace mln const unsigned watch_dog, const unsigned n_times) { - trace::entering("mln::clustering::kmean3d::kmean3d"); + trace::entering("mln::clustering::kmean3d::cstor"); mln_precondition(point.is_valid()); _k_center = k_center; @@ -520,7 +518,7 @@ namespace mln _mean_cnv.append(mean_set); } - trace::exiting("mln::clustering::kmean3d::kmean3d"); + trace::exiting("mln::clustering::kmean3d::cstor"); } //-------------------------------------------------------------------------- @@ -1338,7 +1336,7 @@ namespace mln trace::exiting("mln::clustering::kmean3d::launch_n_times"); } -#endif // ! MLN_INCLUDE_ONLY +# endif // ! MLN_INCLUDE_ONLY } // end of namespace mln::clustering diff --git a/scribo/sandbox/green/mln/clustering/kmean_rgb.hh b/scribo/sandbox/green/mln/clustering/kmean_rgb.hh index 253745b..544066b 100644 --- a/scribo/sandbox/green/mln/clustering/kmean_rgb.hh +++ b/scribo/sandbox/green/mln/clustering/kmean_rgb.hh @@ -1,4 +1,4 @@ -// Copyright (C) 2008,2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -28,16 +28,68 @@ /// \file /// -/// \brief Implements the optimized kmean algorithm. +/// \brief Implements the optimized kmean algorithm in the 3d-RGB space. /// /// This algorithm is optimized in the way it proceeds directly with -/// the rgb values inspite of the pixel attribute. The +/// the rgb value inspite of the pixel attribute. The /// algorithm is independant from the image dimension. But, we have to /// compute one time the histogram. In fact, we move a recurrent cost -/// to a fix cost in the complexity. This version is adapted to -/// image with small quantification. - -/// APLATISSEMENT DES KMEAN3D +/// by a fix cost in the complexity. This version is very adapted to +/// images with small quantification. But, in 3d, the execution +/// becomes very slow. It's just normal because the quantification is +/// n bits per axis. So the actual histogram may be bigger than the image. +/// +/// Take care to the following point: The within variance is still a +/// scalar value because we take the distance between two points and +/// the result is a scalar from the geometrical point of view. An +/// alternative implementation could study the variance/covariance +/// matrix of each sub data clouds and works with the trace of the +/// within variance matrix (as we do for the fisher criteria in N-d). +/// +/// The following sample is a typical use of the functional (versus +/// object) kmean implementation. +/// +/// #include <iostream> +/// #include <mln/clustering/kmean_rgb.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/data/transform.hh> +/// #include <mln/fun/v2v/rgb8_to_rgbn.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/ppm/load.hh> +/// #include <mln/value/label_8.hh> +/// #include <mln/value/rgb.hh> +/// #include <mln/value/rgb8.hh> +/// +/// int main() +/// { +/// typedef mln::value::label_8 t_lbl8; +/// typedef mln::value::rgb8 t_rgb8; +/// typedef mln::value::rgb<5> t_rgb5; +/// typedef mln::image2d<t_rgb8> t_image2d_rgb8; +/// typedef mln::image2d<t_rgb5> t_image2d_rgb5; +/// typedef mln::image2d<t_lbl8> t_image2d_lbl8; +/// typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; +/// +/// t_image2d_rgb8 img_rgb8; +/// t_image2d_rgb5 img_rgb5; +/// t_image2d_lbl8 img_lbl8; +/// +/// mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); +/// +/// img_rgb5 = mln::data::transform(img_rgb8, t_rgb8_to_rgb5()); +/// img_lbl8 = mln::clustering::kmean_rgb<double,5>(img_rgb5, 3); +/// +/// return 0; +/// } +/// +/// \fixme The execution shows a bug in printing outputs and it seems severe. +/// +/// The last execution with the following set of parameters +/// {house.ppm,3,10,10} shows that if the binary starts correctly, it +/// ends before returning the label image and with disturbing outputs. +/// Dumping the outputs in a file reveals that the number of +/// trace::entering differs from the number of trace::exiting. May the +/// program exit from a loop without ending a trace ??? # include <limits.h> # include <iostream> @@ -86,7 +138,7 @@ //-------------------------------------------------------------------------- -// CODE APLATI +// FUNCTIONAL CODE //-------------------------------------------------------------------------- @@ -95,7 +147,24 @@ namespace mln namespace clustering { - + /// \brief Implements the functional kmean algorithm. + /// + /// This functional version of the kmean is very specific. All the + /// code is view as a one-block function. This code is provided as + /// is. I (YJ) don't know where i stopped this version. It may not + /// work. Debugging tools are not yet provided. This code is just + /// the functional transcription of the kmean3d version. The code + /// has the very experimental status. + /// + /// T is the type used for computations (float or double). + /// n is the quantification for the rgb image. + /// + /// \param[in] point : the image as the population of pixels. + /// \param[in] k_center : the number of centers. + /// \param[in] watch_dog : the limit to observe the convergence (10). + /// \param[in] n_times : the number of times that we executed kmean(10). + /// + /// \return an image which represents the pixel classification. template <typename T, unsigned n, typename I> inline image2d<value::label_8> diff --git a/scribo/sandbox/green/mln/fun/v2v/rg_to_rgb.hh b/scribo/sandbox/green/mln/fun/v2v/rg_to_rgb.hh index ca7ba51..e89edad 100644 --- a/scribo/sandbox/green/mln/fun/v2v/rg_to_rgb.hh +++ b/scribo/sandbox/green/mln/fun/v2v/rg_to_rgb.hh @@ -1,6 +1,4 @@ -// Copyright (C) 2007 EPITA Research and Development Laboratory (LRDE) -// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) -// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE // // This file is part of Olena. // @@ -34,7 +32,51 @@ /// \file /// -/// \brief Convert rg value to rgb +/// \brief Convert a rg value to a rgb value. +/// +/// This source implements the conversion between rg space and rgb space. +/// +/// The following sample is a typical use of the rg/rgb conversion function. +/// +/// #include <iostream> +/// #include <mln/clustering/kmean2d.hh> +/// #include <mln/core/image/image2d.hh> +/// #include <mln/data/transform.hh> +/// #include <mln/fun/v2v/rgb_to_rg.hh> +/// #include <mln/fun/v2v/rg_to_rgb.hh> +/// #include <mln/img_path.hh> +/// #include <mln/io/ppm/load.hh> +/// #include <mln/value/rg.hh> +/// #include <mln/value/rgb8.hh> +/// +/// int main() +/// { +/// typedef mln::value::rg<8> t_rg8; +/// typedef mln::value::rgb8 t_rgb8; +/// typedef mln::image2d<t_rgb8> t_image2d_rgb8; +/// typedef mln::image2d<t_rg8> t_image2d_rg8; +/// typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; +/// typedef mln::fun::v2v::rg_to_rgb<8> t_rg_to_rgb; +/// +/// t_image2d_rgb8 img_rgb8; +/// t_image2d_rgb8 point_img_rgb8; +/// t_image2d_rg8 img_rg8; +/// +/// mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); +/// +/// img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); +/// +/// mln::clustering::kmean2d<double, 8> kmean(img_rg8, 3); +/// +/// kmean.launch_n_times(); +/// +/// mln::clustering::kmean2d<double,8>::t_point_img point_img = +/// kmean.get_point(); +/// +/// point_img_rgb8 = mln::data::transform(point_img, t_rg_to_rgb()); +/// +/// return 0; +/// } namespace mln { @@ -47,14 +89,21 @@ namespace mln /// \brief Convert rg value to rgb. /// + /// Param n defines the quantification used for rgb space and rg space. + /// /// \ingroup modfunv2v - template <unsigned n> struct rg_to_rgb : Function_v2v< rg_to_rgb<n> > { typedef value::rg<n> argument; typedef value::rgb<n> result; + /// \brief Convert rg value to rgb value. + /// + /// \param[in] v the rg value to convert. + /// + /// Conversion is done by calling the rgb constructor and fill + /// the empty attirbute by 127. result operator()(const argument& v) const { return value::rgb<n>(v.red(), v.green(), 127); diff --git a/scribo/sandbox/green/tests/clustering/k_mean/Makefile.am b/scribo/sandbox/green/tests/clustering/k_mean/Makefile.am index 5f00678..77f9015 100644 --- a/scribo/sandbox/green/tests/clustering/k_mean/Makefile.am +++ b/scribo/sandbox/green/tests/clustering/k_mean/Makefile.am @@ -6,8 +6,13 @@ # TOOLS # ######### -INCLUDES= -I$(HOME)/svn/oln/trunk/milena/sandbox/green +#LOADLIBES= -lboost_filesystem +INCLUDES1= -I$(HOME)/git/olena/scribo/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) ECHO= echo RM= rm MKDIR= mkdir -p @@ -20,10 +25,10 @@ BUILD__PATTERN= green/build/tests 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 @@ -89,7 +94,7 @@ $(OBJ_F_PATH):$(SRC_F_PATH) ######### # Force every time the deletion -clean: print clean_target clean_obj clean_dst clean_old #clean_make +clean: clean_target clean_obj clean_dst clean_old #clean_make clean_target: diff --git a/scribo/sandbox/green/tests/clustering/k_mean/k_mean.cc b/scribo/sandbox/green/tests/clustering/k_mean/k_mean.cc index 27751fd..a839ece 100644 --- a/scribo/sandbox/green/tests/clustering/k_mean/k_mean.cc +++ b/scribo/sandbox/green/tests/clustering/k_mean/k_mean.cc @@ -1,34 +1,68 @@ -// UNARY TESTS ON K_MEAN - -#include <mln/clustering/k_mean.hh> +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief This source manages unitary testing on the kmean implementation. +/// +/// Tests are performed from 2 bits up to 8 bits quantification. The goal +/// is to go through each programmatic flow to verify the conformity of the +/// code. It sounds like a theoritic model of unitary testing for milena. +/// The last test enables statistic computations on that component. #include <iostream> -#include <mln/pw/value.hh> - -#include <mln/value/int_u8.hh> -#include <mln/value/rgb8.hh> - #include <mln/literal/colors.hh> #include <mln/algebra/vec.hh> #include <mln/algebra/mat.hh> +#include <mln/clustering/k_mean.hh> + #include <mln/core/macros.hh> #include <mln/core/contract.hh> #include <mln/core/image/image2d.hh> #include <mln/core/image/dmorph/image_if.hh> +#include <mln/data/transform.hh> + +#include <mln/img_path.hh> + #include <mln/io/ppm/load.hh> #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> #include <mln/io/ppm/save.hh> -#include <mln/data/transform.hh> - #include <mln/trait/value/print.hh> #include <mln/trait/image/print.hh> +#include <mln/pw/value.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/rgb8.hh> + #define SIZE_IMAGE 512 #define SIZE_SAMPLE1 (512*512) #define SIZE_SAMPLE2 4 @@ -44,20 +78,18 @@ #define MAT_GROUP2 mln::algebra::mat<SIZE_SAMPLE2, NB_CENTER, TYPE_POINT> #define VEC_VAR mln::algebra::vec<NB_CENTER, TYPE_POINT> - -void test_instantiation() -{ - mln::clustering::k_mean<SIZE_SAMPLE2,NB_CENTER, DIM_POINT, TYPE_POINT> kmean; - - // test the compilation - - std::cout << "test instantiation : ok" << std::endl; -} +/// Tools. +/// \{ +/// \brief Define some tools which are used in unitary testing. +/// +/// This kind of tools help to fill an image with only 4 tons or +/// computing a distance between two colors. Just keep an eye, nothing +/// really difficult in this code. struct rgb8_to_4colors : mln::Function_v2v<rgb8_to_4colors> { typedef mln::value::rgb8 result; - + mln::value::rgb8 operator()(const mln::value::rgb8& color) const { mln::value::rgb8 result; @@ -78,11 +110,12 @@ struct rgb8_to_4colors : mln::Function_v2v<rgb8_to_4colors> void print_color(const mln::value::rgb8& color) { - std::cout << "{r=" << color.red() << ", "; + std::cout << "{r=" << color.red() << ", "; std::cout << "g=" << color.green() << ", "; std::cout << "b=" << color.blue() << "}" << std::endl; } + void fill_image_with_4colors(mln::image2d<mln::value::rgb8>& img) { const mln::value::rgb8 lime = mln::literal::lime; @@ -98,6 +131,140 @@ void fill_image_with_4colors(mln::image2d<mln::value::rgb8>& img) //print_color(purple); } + +double dist(mln::value::rgb8 color1, mln::value::rgb8 color2) +{ + double red = color1.red() - color2.red(); + double green = color1.green() - color2.green(); + double blue = color1.blue() - color2.blue(); + double result= red * red + green * green + blue * blue; + + return result; +} + +/// \} + + +/// External mutators. +/// \{ +/// \brief Replace the kmean data structure values. +/// +/// This is a hack that provides low level routines to access key data +/// structure like point, center, distance and group. + +void set_point(MAT_POINT2& point, + const unsigned index, + const mln::value::rgb8& color) +{ + point(index,0) = color.red(); + point(index,1) = color.green(); + point(index,2) = color.blue(); +} + +void set_center(MAT_CENTER& center, + const unsigned index, + const mln::value::rgb8& color) +{ + center(index,0) = color.red(); + center(index,1) = color.green(); + center(index,2) = color.blue(); +} + +void set_distance(MAT_DISTANCE2& distance, + const unsigned index, + const double d1, + const double d2) +{ + distance(index,0) = d1; + distance(index,1) = d2; +} + +void set_group(MAT_GROUP2& group, + const unsigned index, + const unsigned min) +{ + group(index, min) = 1.0; + group(index, 1-min) = 0.0; +} + +/// \} + +/// Fake states. +/// \{ +/// \brief Help to build from scratch temporary states for the kmean algorithm. +/// +/// This hack allow us to build temporary results used in the previous step to +/// make us very the behaviour of the current step. There is a fake state for +/// every key data structure (point, group, center and distance). + +void fake_init_point(MAT_POINT2& point, + const mln::value::rgb8& point1, + const mln::value::rgb8& point2, + const mln::value::rgb8& point3, + const mln::value::rgb8& point4) +{ + set_point(point, 0, point1); + set_point(point, 1, point2); + set_point(point, 2, point3); + set_point(point, 3, point4); +} + + +void fake_update_group(MAT_GROUP2& group, + const unsigned& point1_min, + const unsigned& point2_min, + const unsigned& point3_min, + const unsigned& point4_min) +{ + set_group(group, 0, point1_min); + set_group(group, 1, point2_min); + set_group(group, 2, point3_min); + set_group(group, 3, point4_min); +} + + +void fake_init_center(MAT_CENTER& center, + const mln::value::rgb8 center1, + const mln::value::rgb8 center2) +{ + + set_center(center, 0, center1); + set_center(center, 1, center2); +} + + + + +void fake_update_distance(MAT_DISTANCE2& distance, + const mln::value::rgb8& point1, + const mln::value::rgb8& point2, + const mln::value::rgb8& point3, + const mln::value::rgb8& point4, + const mln::value::rgb8& center1, + const mln::value::rgb8& center2) +{ + set_distance(distance, 0, dist(point1, center1), dist(point1, center2)); + set_distance(distance, 1, dist(point2, center1), dist(point2, center2)); + set_distance(distance, 2, dist(point3, center1), dist(point3, center2)); + set_distance(distance, 3, dist(point4, center1), dist(point4, center2)); + + /* + for (int i = 0; i < SIZE_SAMPLE2; ++i) + for (int j = 0; j < NB_CENTER; ++j) + std::cout << "d(" << i << "," << j << ") = " << distance(i,j) <<std::endl; + */ +} + +/// \} + +/// Equivalence. +/// \{ +/// \brief Test equivalence between point and image, center and color. +/// +/// Two kinds of equivalence are defined here. The first one tests the +/// equality between the data cloud and the initial image, while the +/// second one tests the equality between a center and a color. + bool is_equivalent(const mln::image2d<mln::value::rgb8>& img, const MAT_POINT1& point) { @@ -118,11 +285,11 @@ bool is_equivalent(const mln::image2d<mln::value::rgb8>& img, if (!test) { std::cout << pi; - std::cout << "{r=" << img(pi).red() << ", "; + std::cout << "{r=" << img(pi).red() << ", "; std::cout << "g=" << img(pi).green() << ", "; std::cout << "b=" << img(pi).blue() << "}"; std::cout << index; - std::cout << "[r=" << point(index,0) << ", "; + std::cout << "[r=" << point(index,0) << ", "; std::cout << "g=" << point(index,1) << ", "; std::cout << "b=" << point(index,2) << "]" << std::endl; @@ -135,6 +302,44 @@ bool is_equivalent(const mln::image2d<mln::value::rgb8>& img, return result; } +bool is_equal(const mln::value::rgb8& ref, + const MAT_CENTER& center, + const unsigned i) +{ + bool result = true; + + result = result && (center(i, 0) - ref.red() < 1.0); + result = result && (center(i, 1) - ref.green() < 1.0); + result = result && (center(i, 2) - ref.blue() < 1.0); + + return result; +} + +/// \} + + +/// kmean unitary testing +/// \{ +/// \brief This part of the code manages the unitary testing. +/// +/// Many tests are performed and new kind of unitary testing +/// appears. In fact, we must simulate the previous steps of the line +/// currently testing and so making some fake steps. Bad and deep +/// hacking in data structure are required. We test the instantiation +/// of the kmean object, its initialization (points and centers), its +/// core routines (update_center, update_group, update_distance, +/// update_variance) and the final loop. + +void test_instantiation() +{ + mln::clustering::k_mean<SIZE_SAMPLE2,NB_CENTER, DIM_POINT, TYPE_POINT> kmean; + + // test the compilation + + std::cout << "test instantiation : ok" << std::endl; +} + + void test_init_point() { typedef mln::value::rgb8 rgb8; @@ -147,45 +352,12 @@ void test_init_point() fill_image_with_4colors(img_ref); kmean.init_point(img_ref); - + mln_assertion(true == is_equivalent(img_ref, kmean.get_point())); std::cout << "Test init point : ok" << std::endl; } -void set_point(MAT_POINT2& point, - const unsigned index, - const mln::value::rgb8& color) -{ - point(index,0) = color.red(); - point(index,1) = color.green(); - point(index,2) = color.blue(); -} - -void fake_init_point(MAT_POINT2& point, - const mln::value::rgb8& point1, - const mln::value::rgb8& point2, - const mln::value::rgb8& point3, - const mln::value::rgb8& point4) -{ - set_point(point, 0, point1); - set_point(point, 1, point2); - set_point(point, 2, point3); - set_point(point, 3, point4); -} - -bool is_equal(const mln::value::rgb8& ref, - const MAT_CENTER& center, - const unsigned i) -{ - bool result = true; - - result = result && (center(i, 0) - ref.red() < 1.0); - result = result && (center(i, 1) - ref.green() < 1.0); - result = result && (center(i, 2) - ref.blue() < 1.0); - - return result; -} void test_init_center() { @@ -195,10 +367,10 @@ void test_init_center() const mln::value::rgb8 brown = mln::literal::brown; const mln::value::rgb8 teal = mln::literal::teal; const mln::value::rgb8 purple = mln::literal::purple; - + fake_init_point(kmean.get_point(), lime, brown, teal, purple); kmean.init_center(); - + mln_assertion(is_equal(lime, kmean.get_center(), 0) || is_equal(brown, kmean.get_center(), 0) || is_equal(teal, kmean.get_center(), 0) || @@ -212,34 +384,6 @@ void test_init_center() std::cout << "Test init center : ok" << std::endl; } -void set_center(MAT_CENTER& center, - const unsigned index, - const mln::value::rgb8& color) -{ - center(index,0) = color.red(); - center(index,1) = color.green(); - center(index,2) = color.blue(); -} - -void fake_init_center(MAT_CENTER& center, - const mln::value::rgb8 center1, - const mln::value::rgb8 center2) -{ - - set_center(center, 0, center1); - set_center(center, 1, center2); -} - - -double dist(mln::value::rgb8 color1, mln::value::rgb8 color2) -{ - double red = color1.red() - color2.red(); - double green = color1.green() - color2.green(); - double blue = color1.blue() - color2.blue(); - double result= red * red + green * green + blue * blue; - - return result; -} void test_update_distance() { @@ -269,34 +413,6 @@ void test_update_distance() std::cout << "Test update distance : ok" << std::endl; } -void set_distance(MAT_DISTANCE2& distance, - const unsigned index, - const double d1, - const double d2) -{ - distance(index,0) = d1; - distance(index,1) = d2; -} - -void fake_update_distance(MAT_DISTANCE2& distance, - const mln::value::rgb8& point1, - const mln::value::rgb8& point2, - const mln::value::rgb8& point3, - const mln::value::rgb8& point4, - const mln::value::rgb8& center1, - const mln::value::rgb8& center2) -{ - set_distance(distance, 0, dist(point1, center1), dist(point1, center2)); - set_distance(distance, 1, dist(point2, center1), dist(point2, center2)); - set_distance(distance, 2, dist(point3, center1), dist(point3, center2)); - set_distance(distance, 3, dist(point4, center1), dist(point4, center2)); - - /* - for (int i = 0; i < SIZE_SAMPLE2; ++i) - for (int j = 0; j < NB_CENTER; ++j) - std::cout << "d(" << i << "," << j << ") = " << distance(i,j) <<std::endl; - */ -} void test_update_group() { @@ -331,26 +447,6 @@ void test_update_group() std::cout << "Test update group : ok" << std::endl; } -void set_group(MAT_GROUP2& group, - const unsigned index, - const unsigned min) -{ - group(index, min) = 1.0; - group(index, 1-min) = 0.0; -} - - -void fake_update_group(MAT_GROUP2& group, - const unsigned& point1_min, - const unsigned& point2_min, - const unsigned& point3_min, - const unsigned& point4_min) -{ - set_group(group, 0, point1_min); - set_group(group, 1, point2_min); - set_group(group, 2, point3_min); - set_group(group, 3, point4_min); -} void test_update_center() { @@ -374,13 +470,14 @@ void test_update_center() fake_update_distance(kmean.get_distance(), lime, brown, teal, purple, c1, c2); fake_update_group(kmean.get_group(), pt1_min, pt2_min, pt3_min, pt4_min); kmean.update_center(); - + mln_assertion(is_equal(mean_c1, kmean.get_center(), 0)); mln_assertion(is_equal(mean_c2, kmean.get_center(), 1)); std::cout << "Test update center : ok" << std::endl; } + void test_update_variance() { mln::clustering::k_mean<SIZE_SAMPLE2, NB_CENTER, DIM_POINT, TYPE_POINT> kmean; @@ -405,13 +502,15 @@ void test_update_variance() fake_update_distance(kmean.get_distance(), lime, brown, teal, purple, c1, c2); fake_update_group(kmean.get_group(), pt1_min, pt2_min, pt3_min, pt4_min); kmean.update_variance(); - + mln_assertion(v1 == var[0]); mln_assertion(v2 == var[1]); std::cout << "Test update variance : ok" << std::endl; } + +/// \fixme this procedure tests actually nothing. void test_loop() { typedef mln::value::rgb8 rgb8; @@ -426,24 +525,22 @@ void test_loop() kmean.init_point(img_ref); kmean.loop(img_ref); - - // std::cout << "Test update variance: ok" << std::endl; + // \FIXME: Which assertion must we define ? + // std::cout << "Test loop : ok" << std::endl; } +/// \} int main() { - //test_instantiation(); - //test_init_point(); - //test_init_center(); - //test_update_distance(); - //test_update_group(); - //test_update_center(); - //test_update_variance(); - - // mln::trace::quiet = false; - + test_instantiation(); + test_init_point(); + test_init_center(); + test_update_distance(); + test_update_group(); + test_update_center(); + test_update_variance(); test_loop(); return 0; diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/clustering/k_mean/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/clustering/k_mean/Makefile.am diff --git a/scribo/sandbox/green/use/clustering/k_mean/k_mean.cc b/scribo/sandbox/green/use/clustering/k_mean/k_mean.cc new file mode 100644 index 0000000..9b00037 --- /dev/null +++ b/scribo/sandbox/green/use/clustering/k_mean/k_mean.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Minimal code to use the first implementation of the kmean algorithm. +/// + +#include <mln/clustering/k_mean.hh> +#include <mln/core/image/image2d.hh> +#include <mln/img_path.hh> +#include <mln/io/ppm/load.hh> +#include <mln/trait/value_.hh> +#include <mln/value/rgb8.hh> + +#define NB_CENTER 9 +#define NB_POINT (128*128) +#define POINT_SIZE mln_dim(mln::value::rgb8) +#define POINT_TYPE double + +int main() +{ + typedef mln::value::rgb8 t_rgb8; + mln::image2d<t_rgb8> img_rgb8; + + mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); + mln::clustering::k_mean<NB_POINT, NB_CENTER, POINT_SIZE, POINT_TYPE> kmean; + + kmean.init_point(img_rgb8); + kmean.loop(img_rgb8); + + return 0; +} diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/clustering/kmean1d/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/clustering/kmean1d/Makefile.am diff --git a/scribo/sandbox/green/use/clustering/kmean1d/kmean1d.cc b/scribo/sandbox/green/use/clustering/kmean1d/kmean1d.cc new file mode 100644 index 0000000..82b13c9 --- /dev/null +++ b/scribo/sandbox/green/use/clustering/kmean1d/kmean1d.cc @@ -0,0 +1,50 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Minimal code to use the second implementation of the kmean algorithm. +/// + +#include <iostream> +#include <mln/clustering/kmean1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/img_path.hh> +#include <mln/io/pgm/load.hh> +#include <mln/value/int_u8.hh> + +int main() +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + t_image2d_int_u8 img_int_u8; + + mln::io::pgm::load(img_int_u8, OLENA_IMG_PATH"/house.pgm"); + mln::clustering::kmean1d<double, 8> kmean(img_int_u8, 3); + + kmean.launch_n_times(); + + return 0; +} diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/clustering/kmean2d/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/clustering/kmean2d/Makefile.am diff --git a/scribo/sandbox/green/use/clustering/kmean2d/kmean2d.cc b/scribo/sandbox/green/use/clustering/kmean2d/kmean2d.cc new file mode 100644 index 0000000..d6bf05b --- /dev/null +++ b/scribo/sandbox/green/use/clustering/kmean2d/kmean2d.cc @@ -0,0 +1,61 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Minimal code to use the optimized version of the kmean2d algorithm. +/// + +#include <iostream> +#include <mln/clustering/kmean2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/data/transform.hh> +#include <mln/fun/v2v/rgb_to_rg.hh> +#include <mln/img_path.hh> +#include <mln/io/ppm/load.hh> +#include <mln/value/rg.hh> +#include <mln/value/rgb8.hh> + +int main() +{ + typedef mln::value::rg<8> t_rg8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rg8> t_image2d_rg8; + typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; + + t_image2d_rgb8 img_rgb8; + t_image2d_rg8 img_rg8; + + mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); + + img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); + + mln::clustering::kmean2d<double, 8> kmean(img_rg8, 3); + + kmean.launch_n_times(); + + return 0; +} diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/clustering/kmean3d/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/clustering/kmean3d/Makefile.am diff --git a/scribo/sandbox/green/use/clustering/kmean3d/kmean3d.cc b/scribo/sandbox/green/use/clustering/kmean3d/kmean3d.cc new file mode 100644 index 0000000..c57d48a --- /dev/null +++ b/scribo/sandbox/green/use/clustering/kmean3d/kmean3d.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Minimal code to use the optimized version of the kmean3d algorithm. +/// + +#include <iostream> +#include <mln/clustering/kmean3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/data/transform.hh> +#include <mln/fun/v2v/rgb8_to_rgbn.hh> +#include <mln/img_path.hh> +#include <mln/io/ppm/load.hh> +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + + +int main() +{ + typedef mln::clustering::kmean3d<double,5> t_kmean; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<5> t_rgb5; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb5> t_image2d_rgb5; + typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; + + t_image2d_rgb8 img_rgb8; + t_image2d_rgb5 img_rgb5; + + mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); + + img_rgb5=mln::data::transform(img_rgb8, t_rgb8_to_rgb5()); + + t_kmean kmean(img_rgb5, 3); + + kmean.launch_n_times(); + + return 0; +} diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/clustering/kmean_rgb/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/clustering/kmean_rgb/Makefile.am diff --git a/scribo/sandbox/green/use/clustering/kmean_rgb/kmean_rgb.cc b/scribo/sandbox/green/use/clustering/kmean_rgb/kmean_rgb.cc new file mode 100644 index 0000000..3bf2241 --- /dev/null +++ b/scribo/sandbox/green/use/clustering/kmean_rgb/kmean_rgb.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Minimal code to use the the functional (versus object) kmean code. +/// + +#include <iostream> +#include <mln/clustering/kmean_rgb.hh> +#include <mln/core/image/image2d.hh> +#include <mln/data/transform.hh> +#include <mln/fun/v2v/rgb8_to_rgbn.hh> +#include <mln/img_path.hh> +#include <mln/io/ppm/load.hh> +#include <mln/value/label_8.hh> +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + + +int main() +{ + typedef mln::value::label_8 t_lbl8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<5> t_rgb5; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb5> t_image2d_rgb5; + typedef mln::image2d<t_lbl8> t_image2d_lbl8; + typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5; + + t_image2d_rgb8 img_rgb8; + t_image2d_rgb5 img_rgb5; + t_image2d_lbl8 img_lbl8; + + mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); + + img_rgb5 = mln::data::transform(img_rgb8, t_rgb8_to_rgb5()); + img_lbl8 = mln::clustering::kmean_rgb<double,5>(img_rgb5, 3, 10, 10); + + return 0; +} diff --git a/scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am b/scribo/sandbox/green/use/fun/v2v/rg_to_rgb/Makefile.am similarity index 100% copy from scribo/sandbox/green/use/accu/stat/histo1d/Makefile.am copy to scribo/sandbox/green/use/fun/v2v/rg_to_rgb/Makefile.am diff --git a/scribo/sandbox/green/use/fun/v2v/rg_to_rgb/rg_to_rgb.cc b/scribo/sandbox/green/use/fun/v2v/rg_to_rgb/rg_to_rgb.cc new file mode 100644 index 0000000..84ae33f --- /dev/null +++ b/scribo/sandbox/green/use/fun/v2v/rg_to_rgb/rg_to_rgb.cc @@ -0,0 +1,68 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Example of using rg_t_rgb transformation used with kmean2d. +/// + +#include <iostream> +#include <mln/clustering/kmean2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/data/transform.hh> +#include <mln/fun/v2v/rgb_to_rg.hh> +#include <mln/fun/v2v/rg_to_rgb.hh> +#include <mln/img_path.hh> +#include <mln/io/ppm/load.hh> +#include <mln/value/rg.hh> +#include <mln/value/rgb8.hh> + +int main() +{ + typedef mln::value::rg<8> t_rg8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rg8> t_image2d_rg8; + typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; + typedef mln::fun::v2v::rg_to_rgb<8> t_rg_to_rgb; + + t_image2d_rgb8 img_rgb8; + t_image2d_rgb8 point_img_rgb8; + t_image2d_rg8 img_rg8; + + mln::io::ppm::load(img_rgb8, OLENA_IMG_PATH"/house.ppm"); + + img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); + + mln::clustering::kmean2d<double, 8> kmean(img_rg8, 3); + + kmean.launch_n_times(); + + mln::clustering::kmean2d<double,8>::t_point_img point_img = kmean.get_point(); + + point_img_rgb8 = mln::data::transform(point_img, t_rg_to_rgb()); + + return 0; +} -- 1.5.6.5
14Â years, 2Â months
1
0
0
0
last-svn-commit-46-gba64aaf Add some futur materials on regional maxima.
by Yann Jacquelet
* test_labelling_2.cc: New file. * test_labelling_3.cc: New file. --- scribo/sandbox/green/ChangeLog | 7 + .../sandbox/green/test_labelling_2.cc | 318 +++++++------------- .../sandbox/green/test_labelling_3.cc | 25 ++- 3 files changed, 141 insertions(+), 209 deletions(-) copy milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc => scribo/sandbox/green/test_labelling_2.cc (64%) copy milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc => scribo/sandbox/green/test_labelling_3.cc (92%) diff --git a/scribo/sandbox/green/ChangeLog b/scribo/sandbox/green/ChangeLog index 4ddaa54..9e9b7d6 100644 --- a/scribo/sandbox/green/ChangeLog +++ b/scribo/sandbox/green/ChangeLog @@ -1,3 +1,10 @@ +2010-06-30 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Add some futur materials on regional maxima. + + * test_labelling_2.cc: New file. + * test_labelling_3.cc: New file. + 2010-06-28 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Implement the kmean algorithh and start to optimize it. diff --git a/milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc b/scribo/sandbox/green/test_labelling_2.cc similarity index 64% copy from milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc copy to scribo/sandbox/green/test_labelling_2.cc index 30e68b1..28129f1 100644 --- a/milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc +++ b/scribo/sandbox/green/test_labelling_2.cc @@ -1,4 +1,29 @@ -// DEMO ON KMEAN2D +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file #include <mln/clustering/kmean2d.hh> @@ -53,10 +78,10 @@ // Ok pour la convergence. // demander à Théo pour le temps (45 sec pour chaque launching [house,3 cl.]) // -void do_demo_kmean(const std::string& image, - const unsigned k_center, - const unsigned n_times, - const unsigned watch_dog) +void do_demo(const std::string& image, + const unsigned k_center, + const unsigned n_times, + const unsigned watch_dog) { typedef mln::clustering::kmean2d<double,8> t_kmean; typedef mln::value::rg<8> t_rg8; @@ -104,6 +129,21 @@ void do_demo_kmean(const std::string& image, } +void demo(const std::string& image = OLENA_IMG_PATH"/house.ppm", + const unsigned k_center = 3, + const unsigned n_times = 10, + const unsigned watch_dog = 10) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "Launching the demo with these parameters" << std::endl; + std::cout << "image : " << image << std::endl; + std::cout << "k_center : " << k_center << std::endl; + std::cout << "n_times : " << n_times << std::endl; + std::cout << "watch_dog : " << watch_dog << std::endl; + std::cout << "----------------------------------------" << std::endl; + + do_demo(image, k_center, n_times, watch_dog); +} mln::image2d<mln::value::rgb<8> > merge(const mln::image2d<mln::value::rg<8> >& input, @@ -134,124 +174,91 @@ merge(const mln::image2d<mln::value::rg<8> >& input, } -// -// Watershed image processing chain. -// -void do_demo_watershed(const std::string& image, - const unsigned k_center, - const unsigned n_times, - const unsigned watch_dog) -{ - typedef mln::value::label_8 t_lbl8; - typedef mln::value::rg<8> t_rg8; - typedef mln::value::rgb8 t_rgb8; - typedef mln::value::int_u8 t_int_u8; - typedef mln::image2d<t_lbl8> t_image2d_lbl8; - typedef mln::image2d<t_rgb8> t_image2d_rgb8; - typedef mln::image2d<t_int_u8> t_image2d_int_u8; - typedef mln::image2d<t_rg8> t_image2d_rg8; - typedef mln::image2d<unsigned> t_histo2d; - typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; - typedef mln::fun::v2v::rg_to_rgb<8> t_rg_to_rgb; - - t_image2d_rgb8 img_rgb8; - t_image2d_rgb8 output; - t_image2d_rgb8 img_cast; - t_image2d_rg8 img_rg8; - t_histo2d histo; - t_image2d_lbl8 label; - t_lbl8 n_labels; - t_histo2d reverted; - - // IMAGE LOADING PHASE - mln::io::ppm::load(house_rgb8, image.c_str()); - img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); - img_cast = mln::data::transform(img_rg8, t_rg_to_rgb()); - mln::io::ppm::save(img_cast, "red_green.ppm"); - - // HISTO COMPUTING AND FILTERING PHASE - histo = mln::data::compute(mln::accu::meta::stat::histo2d(), img_rg8); - mln::io::plot::save_image_sh(histo, "histo.sh"); - - //histo = mln::morpho::elementary::opening(histo, mln::c4()); - - mln::io::plot::save_image_sh(histo, "histo.sh"); - - // LABELING PHASE - reverted = mln::arith::revert(histo); - label = mln::morpho::watershed::flooding(reverted, mln::c4(), n_labels); - mln::io::pgm::save(label, "label.pgm"); +void usage(const int argc, const char *args[]) +{ + std::cout << "----------------------------------------" << std::endl; + std::cout << "argc : " << argc << std::endl; - // OUTPUT PHASE - output = merge(house_rg8, label); + for (int i = 0; i < argc; ++i) + std::cout << "args[" << i << "] : " << args[i] << std::endl; - mln::io::ppm::save(output, "merge.ppm"); + std::cout << "----------------------------------------" << std::endl; + std::cout << "usage: kmean2d [image [k_center [n_times [watch_dog]]]]" + << std::endl; + std::cout << "pbm image (points to work with)" << std::endl; + std::cout << "unsigned k_center (number of centers)" << std::endl; + std::cout << "unsigned n_times (number of launching)" << std::endl; + std::cout << "unsigned watch_dog (convergence loop)" << std::endl; + std::cout << "----------------------------------------" << std::endl; } - -// -// Regional maxima image processing chain. -// -void do_demo_regional1(const std::string& image, - const unsigned k_center, - const unsigned n_times, - const unsigned watch_dog) +bool char_to_unsigned(const bool status, const char *arg, unsigned& val) { - typedef mln::value::label_8 t_lbl8; - typedef mln::value::rg<8> t_rg8; - typedef mln::value::rgb8 t_rgb8; - typedef mln::value::int_u8 t_int_u8; - typedef mln::image2d<t_lbl8> t_image2d_lbl8; - typedef mln::image2d<t_rgb8> t_image2d_rgb8; - typedef mln::image2d<t_int_u8> t_image2d_int_u8; - typedef mln::image2d<t_rg8> t_image2d_rg8; - typedef mln::image2d<unsigned> t_histo2d; - typedef mln::fun::v2v::rgb_to_rg<8> t_rgb_to_rg; - typedef mln::fun::v2v::rg_to_rgb<8> t_rg_to_rgb; + bool result = false; - t_image2d_rgb8 img_rgb8; - t_image2d_rgb8 output; - t_image2d_rgb8 img_cast; - t_image2d_rg8 img_rg8; - t_histo2d histo; - t_histo2d opened; - t_image2d_lbl8 label; - t_image2d_lbl8 dilated; - t_lbl8 n_labels; + if (status) + { + std::istringstream arg_stream(arg); + arg_stream >> val; - // IMAGE LOADING PHASE - mln::io::ppm::load(img_rgb8, image.c_str()); - img_rg8 = mln::data::transform(img_rgb8, t_rgb_to_rg()); - img_cast = mln::data::transform(img_rg8, t_rg_to_rgb()); - mln::io::ppm::save(img_cast, "red_green.ppm"); + result = !arg_stream.fail(); + } + return result; +} - // HISTO COMPUTING PHASE - histo = mln::data::compute(mln::accu::meta::stat::histo2d(), img_rg8); - mln::io::plot::save_image_sh(histo, "histo.sh"); +bool char_to_string(const bool status, const char *arg, std::string& val) +{ + bool result = false; + if (status) + { + std::istringstream arg_stream(arg); - // HISTO FILTERING PHASE - opened = mln::morpho::elementary::opening(histo, mln::c8()); - mln::io::plot::save_image_sh(opened, "opened.sh"); + arg_stream >> val; + return !arg_stream.fail(); + } - // HISTO LABELING PHASE - label = mln::labeling::regional_maxima(opened, mln::c8(), n_labels); - mln::io::pgm::save(label, "label.pgm"); + return result; +} +int main(const int argc, const char *args[]) +{ + std::string image("top"); + unsigned k_center; + unsigned watch_dog; + unsigned n_times; + bool status = true; - // HISTO FUZZY PHASE - dilated = mln::morpho::elementary::dilation(label, mln::c8()); - mln::io::pgm::save(dilated, "dilated.pgm"); + switch (argc) + { + case 5: status = char_to_unsigned(status, args[4], watch_dog); + case 4: status = char_to_unsigned(status, args[3], n_times); + case 3: status = char_to_unsigned(status, args[2], k_center); + case 2: status = char_to_string(status, args[1], image); break; + case 1: status = true; break; + default: status = false; + } + if (status) + { + switch (argc) + { + case 1: demo(); break; + case 2: demo(image); break; + case 3: demo(image, k_center); break; + case 4: demo(image, k_center, n_times); break; + case 5: demo(image, k_center, n_times, watch_dog); break; + } + } + else + usage(argc, args); - // OUTPUT PHASE - output = merge(img_rg8, dilated); - mln::io::ppm::save(output, "merge.ppm"); + return 0; } // @@ -333,108 +340,3 @@ void do_demo_regional2(const std::string& image, -void demo(const std::string& image = SCRIBO_PPM_IMG_PATH"/mp00082c_50p.ppm", - //const std::string& image = OLENA_IMG_PATH"/house.ppm", - const unsigned k_center = 2, - //const unsigned k_center = 3, - const unsigned n_times = 10, - const unsigned watch_dog = 10) -{ - std::cout << "----------------------------------------" << std::endl; - std::cout << "Launching the demo with these parameters" << std::endl; - std::cout << "image : " << image << std::endl; - std::cout << "k_center : " << k_center << std::endl; - std::cout << "n_times : " << n_times << std::endl; - std::cout << "watch_dog : " << watch_dog << std::endl; - std::cout << "----------------------------------------" << std::endl; - -// do_demo_kmean(image, k_center, n_times, watch_dog); -// do_demo_watershed(image, k_center, n_times, watch_dog); - do_demo_regional1(image, k_center, n_times, watch_dog); - //do_demo_regional2(image, k_center, n_times, watch_dog); -} - -void usage(const int argc, const char *args[]) -{ - std::cout << "----------------------------------------" << std::endl; - std::cout << "argc : " << argc << std::endl; - - for (int i = 0; i < argc; ++i) - std::cout << "args[" << i << "] : " << args[i] << std::endl; - - std::cout << "----------------------------------------" << std::endl; - std::cout << "usage: kmean2d [image [k_center [n_times [watch_dog]]]]" - << std::endl; - std::cout << "pbm image (points to work with)" << std::endl; - std::cout << "unsigned k_center (number of centers)" << std::endl; - std::cout << "unsigned n_times (number of launching)" << std::endl; - std::cout << "unsigned watch_dog (convergence loop)" << std::endl; - std::cout << "----------------------------------------" << std::endl; -} - -bool char_to_unsigned(const bool status, const char *arg, unsigned& val) -{ - bool result = false; - - if (status) - { - std::istringstream arg_stream(arg); - - arg_stream >> val; - - result = !arg_stream.fail(); - } - - return result; -} - -bool char_to_string(const bool status, const char *arg, std::string& val) -{ - bool result = false; - - if (status) - { - std::istringstream arg_stream(arg); - - arg_stream >> val; - - return !arg_stream.fail(); - } - - return result; -} - -int main(const int argc, const char *args[]) -{ - std::string image("top"); - unsigned k_center; - unsigned watch_dog; - unsigned n_times; - bool status = true; - - switch (argc) - { - case 5: status = char_to_unsigned(status, args[4], watch_dog); - case 4: status = char_to_unsigned(status, args[3], n_times); - case 3: status = char_to_unsigned(status, args[2], k_center); - case 2: status = char_to_string(status, args[1], image); break; - case 1: status = true; break; - default: status = false; - } - - if (status) - { - switch (argc) - { - case 1: demo(); break; - case 2: demo(image); break; - case 3: demo(image, k_center); break; - case 4: demo(image, k_center, n_times); break; - case 5: demo(image, k_center, n_times, watch_dog); break; - } - } - else - usage(argc, args); - - return 0; -} diff --git a/milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc b/scribo/sandbox/green/test_labelling_3.cc similarity index 92% copy from milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc copy to scribo/sandbox/green/test_labelling_3.cc index 30e68b1..317832b 100644 --- a/milena/sandbox/green/demo/clustering/kmean2d/kmean2d.cc +++ b/scribo/sandbox/green/test_labelling_3.cc @@ -1,4 +1,27 @@ -// DEMO ON KMEAN2D +// Copyright (C) 2007,2008,2009,2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <
http://www.gnu.org/licenses/
>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. #include <mln/clustering/kmean2d.hh> -- 1.5.6.5
14Â years, 2Â months
1
0
0
0
last-svn-commit-44-g7155a61 Define documentation files.
by Yann Jacquelet
* README.green: New index for source files in scribo/sandbox/green. * README.img: New index for image database. * README.result: New index for histogram database. --- scribo/sandbox/green/ChangeLog | 11 ++- scribo/sandbox/green/{README => README.green} | 58 ++++++++-- scribo/sandbox/green/README.img | 124 +++++++++++++++++++++ scribo/sandbox/green/README.result | 143 +++++++++++++++++++++++++ 4 files changed, 324 insertions(+), 12 deletions(-) rename scribo/sandbox/green/{README => README.green} (89%) create mode 100644 scribo/sandbox/green/README.img create mode 100644 scribo/sandbox/green/README.result diff --git a/scribo/sandbox/green/ChangeLog b/scribo/sandbox/green/ChangeLog index 5a7ceaa..7bb6646 100644 --- a/scribo/sandbox/green/ChangeLog +++ b/scribo/sandbox/green/ChangeLog @@ -1,4 +1,13 @@ -2010-06-23 green <jacquelet(a)lrde.epita.fr> +2010-06-24 Yann Jacquelet <jacquelet(a)lrde.epita.fr> + + Define documentation files. + + * README: Delete this file and replace it by README.green. + * README.green: New index for source files in scribo/sandbox/green. + * README.img: New index for image database. + * README.result: New index for histogram database. + +2010-06-23 Yann Jacquelet <jacquelet(a)lrde.epita.fr> Define gnuplot shell export format. diff --git a/scribo/sandbox/green/README b/scribo/sandbox/green/README.green similarity index 89% rename from scribo/sandbox/green/README rename to scribo/sandbox/green/README.green index 03106d3..3f3c9a9 100644 --- a/scribo/sandbox/green/README +++ b/scribo/sandbox/green/README.green @@ -227,26 +227,62 @@ n'importe quel algorithme type huffman (tous les archiveurs en possède un). -VI MLN ------ +VIII VISUALISATION HISTOGRAMMES 3D +---------------------------------- -a) La sauvegarde des images au format gnuplot shell +==> to do -* mln/io/plot/save_image_sh.hh: Librairie sauvegarde format gnuplot shell. -to do ... +* demo/accu/stat/histo2d +* mln/display/dispay_histo.hh +* mln/display/project_histo.hh +IX KMEANS +--------- + +Ce travail m'avait été demandé par théo. Je le laisse inachevé, quelque part +perdu pendant l'optimisation du code et sa transformation en canevas. + +* mln/clustering/k_mean.hh: Première implémentation avec matrices et vecteurs. +* mln/clustering/kmean1d.hh: Implémentation 1d avec des images. +* mln/clustering/kmean2d.hh: Implémentation 2d avec des images. +* mln/clustering/kmean3d.hh: Implémentation 3d avec des images. +* mln/clustering/kmean_rgb.hh: Impl. 3d aplatie et en cours de mise en canevas. + +* tests/clustering/k_mean: Tests unitaires sur la permière version. +* tests/clustering/kmean1d: Tests unitaire sur la version 1d. + +* demo/clustering/kmean1d: Utilisation de la version 1d. +* demo/clustering/kmean2d: Utilisation de la version 2d. +* demo/clustering/kmean3d: Utilisation de la version 3d. +* demo/clustering/kmean_rgb: Utilisation de la version aplatie. + +* bench/clustering/distance: Comparaison algorithmes d'évaluation des distances. + +==> to do + +X REGIONAL MAXIMA +----------------- + +==> to do + + +XI ANNOTATING +------------- + +==> to do + + +a) La sauvegarde des images au format gnuplot shell + +* mln/io/plot/save_image_sh.hh: Librairie sauvegarde format gnuplot shell. + +to do ... -VII VISUALISATION HISTOGRAMMES 3D ---------------------------------- -c) La visualisation des histogrammes 3d -* demo/accu/stat/histo2d -* mln/display/dispay_histo.hh -* mln/displayproject_histo.hh diff --git a/scribo/sandbox/green/README.img b/scribo/sandbox/green/README.img new file mode 100644 index 0000000..c606a93 --- /dev/null +++ b/scribo/sandbox/green/README.img @@ -0,0 +1,124 @@ +I DESCRIPTION DES BASES D'IMAGES +-------------------------------- + +img ==> Répertoire des bases d'images +img/afp ==> Extrait de la base AFP (50 photos avec texte) +img/annotating-1 ==> Base diversifiée internet trop faible qualité +img/annotating-2 ==> Base diversifiée internet meilleur qualité +img/icdar ==> Base ICDAR en 20p/50p/100p (+quelques ajouts) +img/inim ==> Base complète du cours inim +img/olena ==> Images de tests d'olena (+quelques ajouts) + + +II DESCRIPTION DE CHAQUE BASE +----------------------------- + +A) BASE AFP + +img/afp/magick-pal-30-gen.sh ==> Génération base AFP 30 couleurs (imageMagick) +img/afp/magick-pal-20-gen.sh ==> Génération base AFP 20 couleurs (imageMagick) +img/afp/magick-pal-10-gen.sh ==> Génération base AFP 10 couleurs (imageMagick) + +img/afp/gimp-pal-30-gen.sh ==> Génération base AFP 30 couleurs (Gimp) +img/afp/gimp-pal-20-gen.sh ==> Génération base AFP 20 couleurs (Gimp) +img/afp/gimp-pal-10-gen.sh ==> Génération base AFP 10 couleurs (Gimp) + +Ne pas oublier le script de réduction des couleurs pour Gimp. + +$ more ~/.gimp-2.6/scripts/reduce-color.scm +(define (reduce-color filein fileout nbcol) + (let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filein filein))) + (drawable (car (gimp-image-get-active-layer image))) + ) + (gimp-image-convert-indexed image NO-DITHER MAKE-PALETTE nbcol FALSE FALSE "") + (gimp-image-convert-rgb image) + (gimp-file-save RUN-NONINTERACTIVE image drawable fileout fileout) + (gimp-image-delete image) + )) + +img/afp/magick-pal-30 ==> Base AFP 30 couleurs (imageMagick) +img/afp/magick-pal-20 ==> Base AFP 20 couleurs (imageMagick) +img/afp/magick-pal-10 ==> Base AFP 10 couleurs (imageMagick) + +img/afp/gimp-pal-30 ==> Base AFP 30 couleurs (Gimp) +img/afp/gimp-pal-20 ==> Base AFP 20 couleurs (Gimp) +img/afp/gimp-pal-10 ==> Base AFP 10 couleurs (Gimp) + +img/afp/ppm ==> Base AFP originale convertie en ppm +img/afp/jpg ==> Base AFP originale + + +B) BASE ANNOTATING-1 + +img/annotating-1/screenshot ==> Fond d'écran, capture d'écran +img/annotating-1/slide ==> Transparents commerciaux ou conférences +img/annotating-1/handwritten ==> Ecritures manuscrites +img/annotating-1/map ==> Cartes marines, de régions, de villes +img/annotating-1/photo ==> Photo AFP +img/annotating-1/logo ==> Logos de toute sorte, de toute taille +img/annotating-1/typed ==> Documents textuels impimés +img/annotating-1/fax ==> Faxs +img/annotating-1/bill ==> Factures téléphones, eau ... + + +C) BASE ANNOTATING-2 + +img/annotating-2/slide ==> Transparents commerciaux ou conférences +img/annotating-2/handwritten ==> Ecritures manuscrites +img/annotating-2/logo ==> Logos de toute sorte, de toute taille +img/annotating-2/typed ==> Documents textuels impimés +img/annotating-1/bill ==> Factures téléphones, eau ... + + +D) BASE ICDAR + +img/icdar/100p/pbm ==> Base ICDAR 100% en noir & blanc +img/icdar/100p/pgm ==> Base ICDAR 100% en niveaux de gris +img/icdar/100p/ppm ==> Base ICDAR 100% en couleur +img/icdar/100p/gradient-thin ==> Masques de gradient thin sur base ICDAR 100% +img/icdar/100p/gradient-thick ==> Masques de gradient thick sur base ICDAR 100% + +img/icdar/50p/pbm ==> Base ICDAR 50% en noir & blanc +img/icdar/50p/pgm ==> Base ICDAR 50% en niveaux de gris +img/icdar/50p/ppm ==> Base ICDAR 50% en couleur + +img/icdar/20p/pbm ==> Base ICDAR 20% en noir & blanc +img/icdar/20p/pgm ==> Base ICDAR 20% en niveaux de gris +img/icdar/20p/ppm ==> Base ICDAR 20% en couleur + +img/icdar/20p/crop ==> Plein de crops sur la base ICDAR 20% (couleur) + +img/icdar/magick-pal-30-gen.sh => Génération base ICDAR 30 couleurs(imageMagick) +img/icdar/magick-pal-20-gen.sh => Génération base ICDAR 20 couleurs(imageMagick) +img/icdar/magick-pal-10-gen.sh => Génération base ICDAR 10 couleurs(imageMagick) + +img/icdar/gimp-pal-30-gen.sh ==> Génération base ICDAR 30 couleurs (Gimp) +img/icdar/gimp-pal-20-gen.sh ==> Génération base ICDAR 20 couleurs (Gimp) +img/icdar/gimp-pal-10-gen.sh ==> Génération base ICDAR 10 couleurs (Gimp) + +img/icdar/20p/magick-pal-30 ==> Base ICDAR 20% en noir & blanc +img/icdar/20p/magick-pal-20 ==> Base ICDAR 20% en niveaux de gris +img/icdar/20p/magick-pal-10 ==> Base ICDAR 20% en couleur + +img/icdar/20p/gimp-pal-30 ==> Base ICDAR 20% en noir & blanc +img/icdar/20p/gimp-pal-20 ==> Base ICDAR 20% en niveaux de gris +img/icdar/20p/gimp-pal-10 ==> Base ICDAR 20% en couleur + +img/icdar/20p/text-only ==> Images ICDAR avec textes uniquement +img/icdar/20p/text-color ==> Images ICDAR avec textes et couleurs +img/icdar/20p/text-photo ==> Images ICDAR avec textes et photos + + +E) BASE INIM + +img/inim/bg ==> Arrière plan (ppm) +img/inim/fg ==> Avant plan (ppm) +img/inim/in ==> Images d'entrées (ppm) + + +F) BASE OLENA + +Des images générales provenant d'Olena et d'autres pour tester, pour debugger +dans différents formats. + + diff --git a/scribo/sandbox/green/README.result b/scribo/sandbox/green/README.result new file mode 100644 index 0000000..4038a5b --- /dev/null +++ b/scribo/sandbox/green/README.result @@ -0,0 +1,143 @@ +I DESCRIPTION DES BASES D'IMAGES +-------------------------------- + +result ==> Répertoire des bases d'images +result/annotating/afp ==> Analyse de la base AFP +result/annotating/icdar ==> Analyse de la base ICDAR + + +II DESCRIPTION DES TYPES D'IMAGES +--------------------------------- + +Les deux bases d'images contiennent toutes les deux la même +structure. Les résultats sont donc générés sur les images d'origine +(input) et les images dont on a réduit le nombre de couleurs à +30/20/10 soit par gimp, soit par imageMagick. + +result/annotating/afp/input ==> Résultats base originale AFP +result/annotating/afp/magick-pal-30 ==> Résultats AFP 30 couleurs (imageMagick) +result/annotating/afp/magick-pal-20 ==> Résultats AFP 20 couleurs (imageMagick) +result/annotating/afp/magick-pal-10 ==> Résultats AFP 10 couleurs (imageMagick) +result/annotating/afp/gimp-pal-30 ==> Résultats AFP 30 couleurs (Gimp) +result/annotating/afp/gimp-pal-20 ==> Résultats AFP 20 couleurs (Gimp) +result/annotating/afp/gimp-pal-10 ==> Résultats AFP 10 couleurs (Gimp) + +result/annotating/icdar/input ==> Résultats base originale ICDAR +result/annotating/icdar/magick-pal-30 => Résultats ICDAR 30 clrs (imageMagick) +result/annotating/icdar/magick-pal-20 => Résultats ICDAR 20 clrs (imageMagick) +result/annotating/icdar/magick-pal-10 => Résultats ICDAR 10 clrs (imageMagick) +result/annotating/icdar/gimp-pal-30 ==> Résultats ICDAR 30 couleurs (Gimp) +result/annotating/icdar/gimp-pal-20 ==> Résultats ICDAR 20 couleurs (Gimp) +result/annotating/icdar/gimp-pal-10 ==> Résultats ICDAR 10 couleurs (Gimp) + + +II DESCRIPTION DES COMPOSANTES COULEURS +--------------------------------------- + +Pour chaque type d'image, on extraira ensuite les différents composantes +rouge (r), verte (v), bleue (b), teinte (h), saturation (s) et valeur (v). + +result/annotating/afp/input/r ==> comp. rouge base AFP +result/annotating/afp/input/g ==> comp. verte base AFP +result/annotating/afp/input/b ==> comp. bleue base AFP +result/annotating/afp/input/h ==> comp. teinte base AFP +result/annotating/afp/input/s ==> comp. saturation base AFP +result/annotating/afp/input/v ==> comp. valeur base AFP + +result/annotating/afp/magick-pal-30/r ==> comp. rouge AFP 30 clr.(imageMagick) +result/annotating/afp/magick-pal-30/g ==> comp. verte AFP 30 clr.(imageMagick) +result/annotating/afp/magick-pal-30/b ==> comp. bleue AFP 30 clr.(imageMagick) +result/annotating/afp/magick-pal-30/h ==> comp. teinte AFP 30 c.(imageMagick) +result/annotating/afp/magick-pal-30/s ==> comp. sat. AFP 30 clr.(imageMagick) +result/annotating/afp/magick-pal-30/v ==> comp. val. AFP 30 clr.(imageMagick) + +result/annotating/afp/magick-pal-20/r ==> comp. rouge AFP 20 clr.(imageMagick) +result/annotating/afp/magick-pal-20/g ==> comp. verte AFP 20 clr.(imageMagick) +result/annotating/afp/magick-pal-20/b ==> comp. bleue AFP 20 clr.(imageMagick) +result/annotating/afp/magick-pal-20/h ==> comp. teinte AFP 20 c.(imageMagick) +result/annotating/afp/magick-pal-20/s ==> comp. sat. AFP 20 clr.(imageMagick) +result/annotating/afp/magick-pal-20/v ==> comp. val. AFP 20 clr.(imageMagick) + +result/annotating/afp/magick-pal-10/r ==> comp. rouge AFP 10 clr.(imageMagick) +result/annotating/afp/magick-pal-10/g ==> comp. verte AFP 10 clr.(imageMagick) +result/annotating/afp/magick-pal-10/b ==> comp. bleue AFP 10 clr.(imageMagick) +result/annotating/afp/magick-pal-10/h ==> comp. teinte AFP 10 c.(imageMagick) +result/annotating/afp/magick-pal-10/s ==> comp. sat. AFP 10 clr.(imageMagick) +result/annotating/afp/magick-pal-10/v ==> comp. val. AFP 10 clr.(imageMagick) + +result/annotating/afp/gimp-pal-30/r ==> comp. rouge AFP 30 clr.(Gimp) +result/annotating/afp/gimp-pal-30/g ==> comp. verte AFP 30 clr.(Gimp) +result/annotating/afp/gimp-pal-30/b ==> comp. bleue AFP 30 clr.(Gimp) +result/annotating/afp/gimp-pal-30/h ==> comp. teinte AFP 30 clr.(Gimp) +result/annotating/afp/gimp-pal-30/s ==> comp. saturation AFP 30 clr.(Gimp) +result/annotating/afp/gimp-pal-30/v ==> comp. valeur AFP 30 clr.(Gimp) + +result/annotating/afp/gimp-pal-20/r ==> comp. rouge AFP 20 clr.(Gimp) +result/annotating/afp/gimp-pal-20/g ==> comp. verte AFP 20 clr.(Gimp) +result/annotating/afp/gimp-pal-20/b ==> comp. bleue AFP 20 clr.(Gimp) +result/annotating/afp/gimp-pal-20/h ==> comp. teinte AFP 20 clr.(Gimp) +result/annotating/afp/gimp-pal-20/s ==> comp. saturation AFP 20 clr.(Gimp) +result/annotating/afp/gimp-pal-20/v ==> comp. valeur AFP 20 clr.(Gimp) + +result/annotating/icdar/input/r ==> comp. rouge base ICDAR +result/annotating/icdar/input/g ==> comp. verte base ICDAR +result/annotating/icdar/input/b ==> comp. bleue base ICDAR +result/annotating/icdar/input/h ==> comp. teinte base ICDAR +result/annotating/icdar/input/s ==> comp. saturation base ICDAR +result/annotating/icdar/input/v ==> comp. valeur base ICDAR + +result/annotating/icdar/magick-pal-30/r => comp. rouge ICDAR 30 c.(imageMagick) +result/annotating/icdar/magick-pal-30/g => comp. verte ICDAR 30 c.(imageMagick) +result/annotating/icdar/magick-pal-30/b => comp. bleue ICDAR 30 c.(imageMagick) +result/annotating/icdar/magick-pal-30/h => comp. teinte ICDAR 30 c.(imageMagick) +result/annotating/icdar/magick-pal-30/s => comp. sat. ICDAR 30 c.(imageMagick) +result/annotating/icdar/magick-pal-30/v => comp. val. ICDAR 30 c.(imageMagick) + +result/annotating/icdar/magick-pal-20/r => comp. rouge ICDAR 20 c.(imageMagick) +result/annotating/icdar/magick-pal-20/g => comp. verte ICDAR 20 c.(imageMagick) +result/annotating/icdar/magick-pal-20/b => comp. bleue ICDAR 20 c.(imageMagick) +result/annotating/icdar/magick-pal-20/h => comp. teinte ICDAR 20 c.(imageMagick) +result/annotating/icdar/magick-pal-20/s => comp. sat. ICDAR 20 c.(imageMagick) +result/annotating/icdar/magick-pal-20/v => comp. val. ICDAR 20 c.(imageMagick) + +result/annotating/icdar/magick-pal-10/r => comp. rouge ICDAR 10 c.(imageMagick) +result/annotating/icdar/magick-pal-10/g => comp. verte ICDAR 10 c.(imageMagick) +result/annotating/icdar/magick-pal-10/b => comp. bleue ICDAR 10 c.(imageMagick) +result/annotating/icdar/magick-pal-10/h => comp. teinte ICDAR 10 c.(imageMagick) +result/annotating/icdar/magick-pal-10/s => comp. sat. ICDAR 10 c.(imageMagick) +result/annotating/icdar/magick-pal-10/v => comp. val. ICDAR 10 c.(imageMagick) + +result/annotating/icdar/gimp-pal-30/r => comp. rouge ICDAR 30 c.(Gimp) +result/annotating/icdar/gimp-pal-30/g => comp. verte ICDAR 30 c.(Gimp) +result/annotating/icdar/gimp-pal-30/b => comp. bleue ICDAR 30 c.(Gimp) +result/annotating/icdar/gimp-pal-30/h => comp. teinte ICDAR 30 c.(Gimp) +result/annotating/icdar/gimp-pal-30/s => comp. saturation ICDAR 30 c.(Gimp) +result/annotating/icdar/gimp-pal-30/v => comp. valeur ICDAR 30 c.(Gimp) + +result/annotating/icdar/gimp-pal-20/r => comp. rouge ICDAR 20 c.(Gimp) +result/annotating/icdar/gimp-pal-20/g => comp. verte ICDAR 20 c.(Gimp) +result/annotating/icdar/gimp-pal-20/b => comp. bleue ICDAR 20 c.(Gimp) +result/annotating/icdar/gimp-pal-20/h => comp. teinte ICDAR 20 c.(Gimp) +result/annotating/icdar/gimp-pal-20/s => comp. saturation ICDAR 20 c.(Gimp) +result/annotating/icdar/gimp-pal-20/v => comp. valeur ICDAR 20 c.(Gimp) + +result/annotating/icdar/gimp-pal-10/r => comp. rouge ICDAR 10 c.(Gimp) +result/annotating/icdar/gimp-pal-10/g => comp. verte ICDAR 10 c.(Gimp) +result/annotating/icdar/gimp-pal-10/b => comp. bleue ICDAR 10 c.(Gimp) +result/annotating/icdar/gimp-pal-10/h => comp. teinte ICDAR 10 c.(Gimp) +result/annotating/icdar/gimp-pal-10/s => comp. saturation ICDAR 10 c.(Gimp) +result/annotating/icdar/gimp-pal-10/v => comp. valeur ICDAR 10 c.(Gimp) + + +III DESCRIPTION DES TYPES DE RESULTAT +------------------------------------- + +Pour chaque composante et pour chaque image de la base, on obtient une +image en niveau de gris correspondant à la composante (pgm) et son +histogramme normalisé (gnuplot shell). L'histogramme normalisé est un +histogramme dont l'aire vaut 1 (c'est une densité en fait, autrement +dit chaque fréquence a été divisée par la somme des fréquences). Un +script add_range.sh, a permis de rajouter les bornes des axes pour la +visualisation pour chacun des histogrammes générés. Cette +représentation des histogrammes des composantes sous forme de densité +permet de comparer visuellement les images des différentes classes. -- 1.5.6.5
14Â years, 2Â months
1
0
0
0
← Newer
1
...
329
330
331
332
333
334
335
...
963
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
Results per page:
10
25
50
100
200