* scribo/src/afp/components.hh,
* scribo/src/afp/link.hh,
* scribo/src/afp/regroup.hh: New.
---
scribo/ChangeLog | 8 ++
scribo/src/afp/components.hh | 255 ++++++++++++++++++++++++++++++++++++++++++
scribo/src/afp/link.hh | 138 +++++++++++++++++++++++
scribo/src/afp/regroup.hh | 84 ++++++++++++++
4 files changed, 485 insertions(+), 0 deletions(-)
create mode 100644 scribo/src/afp/components.hh
create mode 100644 scribo/src/afp/link.hh
create mode 100644 scribo/src/afp/regroup.hh
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index f7a0e58..d35119e 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,13 @@
2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add dedicated routines for AFP's use case.
+
+ * scribo/src/afp/components.hh,
+ * scribo/src/afp/link.hh,
+ * scribo/src/afp/regroup.hh: New.
+
+2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Add new tools in Scribo.
* src/preprocessing/Makefile.am,
diff --git a/scribo/src/afp/components.hh b/scribo/src/afp/components.hh
new file mode 100644
index 0000000..7db8572
--- /dev/null
+++ b/scribo/src/afp/components.hh
@@ -0,0 +1,255 @@
+#include <mln/io/pbm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <mln/extension/adjust.hh>
+#include <mln/extension/fill.hh>
+#include <mln/data/fill.hh>
+#include <mln/accu/shape/bbox.hh>
+
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/pw/value.hh>
+#include <mln/debug/println.hh>
+
+#include <mln/util/timer.hh>
+#include <mln/labeling/foreground.hh>
+#include <mln/labeling/wrap.hh>
+#include <mln/extension/fill.hh>
+#include <mln/data/compare.hh>
+
+
+namespace mln
+{
+
+ template <typename I>
+ unsigned my_find_root(image2d<I>& data, unsigned x)
+ {
+ if (data.element(x).parent == x)
+ return x;
+ else
+ return data.element(x).parent = my_find_root(data,
+ data.element(x).parent);
+ }
+
+
+
+ struct info
+ {
+ unsigned parent;
+ unsigned card;
+ float row_sum, col_sum;
+ point2d p_min, p_max;
+
+ int width() const
+ {
+ return p_max.col() - p_min.col();
+ }
+
+ int height() const
+ {
+ return p_max.row() - p_min.row();
+ }
+
+ void init(unsigned p, int row, int col)
+ {
+ parent = p;
+ card = 1;
+ row_sum = row;
+ col_sum = col;
+ p_min.row() = row;
+ p_max.row() = row;
+ p_min.col() = col;
+ p_max.col() = col;
+ }
+
+ void update(info& r)
+ {
+ r.parent = this->parent;
+ card += r.card;
+ row_sum += r.row_sum;
+ col_sum += r.col_sum;
+
+ // bkd browsing => p is always higher (lower row) than r
+ mln_invariant(p_min.row() <= r.p_min.row());
+
+ if (r.p_min.col() < p_min.col())
+ p_min.col() = r.p_min.col();
+ if (r.p_max.row() > p_max.row())
+ p_max.row() = r.p_max.row();
+ if (r.p_max.col() > p_max.col())
+ p_max.col() = r.p_max.col();
+ }
+ };
+
+
+
+ template <typename V>
+ image2d<V>
+ extract_components(const image2d<bool>& input,
+ V& nlabels,
+ util::array<box2d>& bboxes,
+ util::array<point2d>& mass_centers)
+ {
+ typedef image2d<bool> I;
+
+ neighb2d nbh = c8();
+ const int
+ nrows = input.nrows(),
+ ncols = input.ncols();
+
+ bboxes.resize(1);
+ mass_centers.resize(1);
+
+ image2d<info> data;
+ image2d<V> label;
+ V current_label = 0;
+ int N, dp_border;
+
+// util::timer time;
+// time.start();
+
+ // init
+ {
+ extension::adjust(input, nbh);
+ N = input.nelements();
+ dp_border = 2 * input.border();
+ extension::fill(input, false);
+ initialize(data, input);
+ }
+
+// float t = time;
+// std::cout << "init = " << t << std::endl;
+// time.restart();
+
+ // 1st pass
+ {
+ util::array<int> dp = positive_offsets_wrt(input, nbh);
+ const unsigned n_nbhs = dp.nelements();
+
+ // Backward.
+ unsigned p = input.index_of_point(point2d(nrows - 1, ncols - 1));
+ for (int row = nrows - 1; row >= 0; --row, p -= dp_border)
+ for (int col = ncols - 1; col >= 0; --col, --p)
+ {
+ if (! input.element(p))
+ continue;
+
+ data.element(p).init(p, row, col); // init
+
+ for (unsigned i = 0; i < n_nbhs; ++i)
+ {
+ unsigned n = p + dp[i];
+ if (! input.element(n))
+ continue;
+ unsigned r = my_find_root(data, n);
+ if (r != p)
+ {
+ data.element(p).update( data.element(r) ); // update
+ }
+ }
+ }
+ }
+
+// t = time;
+// std::cout << "1st pass = " << t << std::endl;
+// time.restart();
+
+ // 2nd pass
+ {
+ initialize(label, input);
+ data::fill(label, 0);
+
+ // Forward.
+ unsigned p = input.index_of_point(point2d(0, 0));
+ for (int row = 0; row < nrows; ++row, p += dp_border)
+ for (int col = 0; col < ncols; ++col, ++p)
+ {
+ if (! input.element(p))
+ continue;
+ const info& dta = data.element(p);
+ if (dta.parent == p)
+ {
+ if (dta.card > 5
+ && (dta.width() >= 1
+ && dta.height() >= 1))
+ {
+ label.element(p) = ++current_label;
+
+ bboxes.append(box2d(dta.p_min, dta.p_max));
+ mass_centers.append(point2d(dta.row_sum / dta.card,
+ dta.col_sum / dta.card));
+ }
+ }
+ else
+ label.element(p) = label.element(dta.parent);
+ }
+ }
+// t = time;
+// std::cout << "2nd pass = " << t << std::endl;
+
+ nlabels = current_label;
+ return label;
+ }
+
+
+} // mln
+
+
+
+// void usage(char* argv[])
+// {
+// std::cerr << argv[0] << " input.pbm output.pgm" <<
std::endl;
+// std::abort();
+// }
+
+
+// int main(int argc, char* argv[])
+// {
+// if (argc != 3)
+// usage(argv);
+
+// using namespace mln;
+
+// image2d<bool> input;
+// io::pbm::load(input, argv[1]);
+
+
+// image2d<unsigned> ref;
+
+// // {
+// // util::timer t;
+// // t.start();
+
+// // unsigned nlabels;
+// // ref = labeling::foreground(input, c4(), nlabels);
+
+// // float ts = t.stop();
+// // std::cout << "tufa: " << ts << " "
<< nlabels << std::endl;
+// // }
+
+// {
+// util::timer t;
+// t.start();
+
+
+// util::array<box2d> bboxes(1, box2d(1,1));
+// util::array<point2d> mass_centers(1, point2d(0,0));
+
+// // util::array<std::pair<box2d, point2d> > data_out(1);
+// unsigned nlabels;
+// image2d<unsigned> comps = extract_components(input, nlabels, bboxes,
mass_centers);
+
+// float ts = t.stop();
+// std::cout << ts << " " << nlabels <<
std::endl;
+
+// // std::cout << bboxes << std::endl;
+// // std::cout << mass_centers << std::endl;
+
+// // if (comps != ref)
+// // std::cout << "diff" << std::endl;
+
+// io::pgm::save(labeling::wrap(value::int_u8(), comps),
+// argv[2]);
+// }
+
+// }
diff --git a/scribo/src/afp/link.hh b/scribo/src/afp/link.hh
new file mode 100644
index 0000000..b899957
--- /dev/null
+++ b/scribo/src/afp/link.hh
@@ -0,0 +1,138 @@
+#include <mln/geom/ncols.hh>
+#include <mln/geom/nrows.hh>
+#include <mln/util/couple.hh>
+#include <scribo/core/object_image.hh>
+#include <scribo/core/macros.hh>
+#include <scribo/primitive/internal/init_link_array.hh>
+
+namespace scribo
+{
+
+ namespace primitive
+ {
+
+ namespace link
+ {
+
+
+ template <typename L>
+ util::couple<object_links<L>, object_links<L> >
+ left_right(const object_image(L)& objects)
+ {
+ object_links<L>
+ right(objects, static_cast<unsigned>(objects.nlabels()) + 1);
+ primitive::internal::init_link_array(right);
+
+ object_links<L>
+ left(objects, static_cast<unsigned>(objects.nlabels()) + 1);
+ primitive::internal::init_link_array(left);
+
+ for_all_components(i, objects.bboxes())
+ {
+ float
+ w = (objects.bbox(i).pmax().col()
+ - objects.bbox(i).pmin().col()),
+ h = (objects.bbox(i).pmax().row()
+ - objects.bbox(i).pmin().row());
+ unsigned dmax = (w / 2.0f) + (3 * math::max(w, h));
+
+
+ const mln_site(L) c = objects.mass_center(i);
+
+ int
+ midcol = (objects.bbox(i).pmax().col()
+ - objects.bbox(i).pmin().col()) / 2;
+ int
+ nrightima = geom::ncols(objects) - c.col(),
+ nleftima = c.col(),
+ nright = std::min(static_cast<unsigned>(nrightima), midcol + dmax),
+ nleft = std::min(static_cast<unsigned>(nleftima), midcol + dmax);
+
+ // Right
+ {
+ const mln_value(L)
+ *p = &objects(c),
+ *pstop = p + nright + 1;
+
+ for (; p != pstop; ++p)
+ {
+ if (*p != literal::zero // Not the background
+ && *p != i // Not the current component
+ && right[*p] != i) // No loops
+ {
+ right[i] = *p;
+ break;
+ }
+ }
+ }
+
+
+ // Left
+ {
+ const mln_value(L)
+ *p = &objects(c),
+ *pstop = p - nleft - 1;
+
+ for (; p != pstop; --p)
+ {
+ if (*p != literal::zero // Not the background
+ && *p != i // Not the current component
+ && left[*p] != i) // No loops
+ {
+ left[i] = *p;
+ break;
+ }
+ }
+ }
+ }
+
+ return mln::make::couple(left, right);
+ }
+
+
+ template <typename L>
+ object_links<L>
+ left(const object_image(L)& objects, unsigned dmax)
+ {
+ object_links<L>
+ left(objects, static_cast<unsigned>(objects.nlabels()) + 1);
+ primitive::internal::init_link_array(left);
+
+ for_all_components(i, objects.bboxes())
+ {
+ const mln_site(L) c = objects.mass_center(i);
+
+ int
+ midcol = (objects.bbox(i).pmax().col()
+ - objects.bbox(i).pmin().col()) / 2;
+ int
+ nleftima = c.col(),
+ nleft = std::min(static_cast<unsigned>(nleftima), midcol + dmax);
+
+ // Left
+ {
+ const mln_value(L)
+ *p = &objects(c),
+ *pstop = p - nleft - 1;
+
+ for (; p != pstop; --p)
+ {
+ if (*p != literal::zero // Not the background
+ && *p != i // Not the current component
+ && left[*p] != i) // No loops
+ {
+ left[i] = *p;
+ break;
+ }
+ }
+ }
+ }
+
+ return left;
+ }
+
+ } // end of namespace scribo::primitive::link
+
+ } // end of namespace scribo::primitive
+
+} // end of namespace scribo
diff --git a/scribo/src/afp/regroup.hh b/scribo/src/afp/regroup.hh
new file mode 100644
index 0000000..c24880b
--- /dev/null
+++ b/scribo/src/afp/regroup.hh
@@ -0,0 +1,84 @@
+#include <mln/geom/ncols.hh>
+#include <mln/geom/nrows.hh>
+#include <mln/util/couple.hh>
+#include <scribo/core/object_image.hh>
+#include <scribo/core/macros.hh>
+#include <scribo/primitive/internal/init_link_array.hh>
+
+namespace scribo
+{
+
+ namespace primitive
+ {
+
+ namespace group
+ {
+
+ template <typename L>
+ object_groups<L>
+ regroup_left(const object_image(L)& objects,
+ const object_groups<L>& groups,
+ unsigned dmax)
+ {
+ trace::entering("scribo::primitive::group::regroup_left");
+
+ mln_precondition(groups.is_valid());
+
+ object_groups<L>
+ new_groups(objects, static_cast<unsigned>(objects.nlabels()) + 1, 0);
+
+ unsigned ngroups = 0;
+ for_all_components(i, objects.bboxes())
+ {
+ if (groups[i] == 0)
+ continue;
+
+ // We MUST set a group id here since the most left group
+ // component won't have any id otherwise.
+ if (new_groups[i] == 0)
+ new_groups[i] = ++ngroups;
+
+ const mln_site(L) c = objects.mass_center(i);
+
+ int
+ midcol = (objects.bbox(i).pmax().col()
+ - objects.bbox(i).pmin().col()) / 2;
+ int
+ nleftima = geom::ncols(objects),
+ nleft = std::min(static_cast<unsigned>(nleftima), midcol + dmax);
+
+ // Left
+ {
+ const mln_value(L)
+ *p = &objects(c),
+ *pstop = p - nleft - 1;
+
+ for (; p != pstop; --p)
+ {
+ if (*p != literal::zero // Not the background
+ && *p != i // Not the current component
+// && new_groups[*p] != ngroups
+ && groups[*p] != 0)
+ {
+ if (new_groups[*p] == 0)
+ new_groups[*p] = ngroups;
+ else
+ {
+ new_groups[i] = new_groups[*p];
+ --ngroups;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ return new_groups;
+ }
+
+ } // end of namespace scribo::primitive::group
+
+ } // end of namespace scribo::primitive
+
+} // end of namespace scribo
--
1.5.6.5