$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
non_text.hh
1 // Copyright (C) 2011, 2013 EPITA Research and Development Laboratory
2 // (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
32 
33 #ifndef SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
34 # define SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
35 
36 # include <mln/morpho/elementary/dilation.hh>
37 
38 
39 # include <scribo/make/text_components_image.hh>
40 # include <scribo/make/text_blocks_image.hh>
41 
42 # include <scribo/primitive/extract/internal/union.hh>
43 # include <scribo/debug/logger.hh>
44 
45 # include <mln/literal/black.hh>
46 
47 //DEBUG
48 #include <mln/util/timer.hh>
49 #include <mln/io/pbm/save.hh>
50 
51 
52 namespace scribo
53 {
54 
55  namespace primitive
56  {
57 
58  namespace extract
59  {
60 
61  using namespace mln;
62 
78  template <typename L>
79  component_set<L>
80  non_text(const document<L>& doc, unsigned nlines);
81 
82 
83 # ifndef MLN_INCLUDE_ONLY
84 
85 
86  namespace internal
87  {
88 
89  template <typename L>
91  learn(const document<L>& doc,
92  const image2d<bool>& txt,
93  const image2d<bool>& txtblocks,
94  unsigned nbits,
95  float p_cover)
96  {
97  const image2d<value::rgb8>& input = doc.image();
98  const image2d<bool>&
99  seps = doc.paragraphs().lines().components().separators();
100 
101  if (txt.border() != input.border()
102  || txtblocks.border() != input.border()
103  || seps.border() != input.border())
104  {
105  std::cerr << " txt.border() = " << txt.border()
106  << " - txtblocks.border() = " << txtblocks.border()
107  << " - input.border() = " << input.border()
108  << " - seps.border() = " << seps.border()
109  << std::endl;
110  std::cerr << "different sizes for borders! Resizing..." << std::endl;
111 
112 
115  border::resize(txtblocks, border::thickness);
117  // std::abort();
118  }
119  extension::fill(input, literal::black);
120 
121  const unsigned q_div = std::pow(2.f, (int)(8 - nbits));
122  const unsigned q = unsigned(std::pow(2.f, (int)nbits));
123  const unsigned nelements = input.nelements();
124 
125 
126  image3d<unsigned> h_bg(q, q, q);
127  data::fill(h_bg, 0);
128 
129  border::fill(txtblocks, false); // so h_bg is not updated by border pixels!
130 
131  unsigned n_bg = 0;
132  {
133  // compute h_bg
134  for (unsigned i = 0; i < nelements; ++i)
135  if (txtblocks.element(i) == true)
136  {
137  ++n_bg;
138  const value::rgb8& c = input.element(i);
139  ++h_bg.at_(c.red() / q_div, c.green() / q_div, c.blue() / q_div);
140  }
141  }
142 
143  typedef std::map<unsigned, unsigned> map_t;
144  map_t ncells_with_nitems;
145  {
146  mln_piter_(box3d) c(h_bg.domain());
147  for_all(c)
148  {
149  unsigned nitems_in_c = h_bg(c);
150  ++ncells_with_nitems[ nitems_in_c ];
151  }
152  }
153 
154 
155  unsigned n_items_min = 0;
156  {
157  map_t::const_reverse_iterator i;
158  unsigned N = 0;
159  for (i = ncells_with_nitems.rbegin(); i != ncells_with_nitems.rend(); ++i)
160  {
161  unsigned nitems = i->first, ncells = i->second;
162  N += nitems * ncells;
163  if (float(N) > p_cover * float(n_bg))
164  {
165  n_items_min = nitems;
166  break;
167  }
168  }
169  }
170  if (n_items_min == 0)
171  n_items_min = 1; // safety
172 
173 
174  image3d<bool> bg(q, q, q);
175  {
176  mln_piter_(box3d) c(h_bg.domain());
177  for_all(c)
178  bg(c) = (h_bg(c) >= n_items_min);
179  }
180 
181 
182  // outputing
183 
184  image2d<bool> output;
185  initialize(output, input);
186  {
187  for (unsigned i = 0; i < nelements; ++i)
188  if (txt.element(i) == true || seps.element(i) == true)
189  output.element(i) = false;
190  else
191  {
192  const value::rgb8& c = input.element(i);
193  output.element(i) = ! bg.at_(c.red() / q_div, c.green() / q_div, c.blue() / q_div);
194  }
195  }
196 
197  return output;
198  }
199 
200 
201 
202 
203 
204  inline
206  cleaning(const image2d<bool>& input, unsigned lambda)
207  {
208  const box2d& dom = input.domain();
209 
210  image2d<unsigned> area(dom);
211  image2d<unsigned> parent(dom);
212  image2d<bool> output(dom);
213 
214  unsigned max_area = 0;
215 
216 
217  // 1st pass = bg union-find
218 
219  {
220  union_find(input, false, // in
221  parent, area, max_area // out
222  );
223  }
224 
225 
226  // 2nd pass = bg biggest component selection
227 
228  {
229  const unsigned nelements = input.nelements();
230  const bool* p_i = input.buffer();
231  bool* p_o = output.buffer();
232  const unsigned* p_a = area.buffer();
233  const unsigned* p_par = parent.buffer();
234 
235  for (unsigned i = 0; i < nelements; ++i)
236  {
237  if (*p_i == true)
238  *p_o = true;
239  else
240  {
241  if (*p_par == i)
242  *p_o = (*p_a != max_area);
243  else
244  *p_o = output.element(*p_par);
245  }
246  ++p_i;
247  ++p_o;
248  ++p_a;
249  ++p_par;
250  }
251  }
252 
253 
254 
255  // 3rd pass = fg union-find
256 
257  {
258  union_find(output, true, // in
259  parent, area, max_area // out
260  );
261  }
262 
263 
264 
265  // 4th pass = cleaning fg
266 
267  {
268  const unsigned nelements = input.nelements();
269  bool* p_o = output.buffer();
270  const unsigned* p_a = area.buffer();
271  const unsigned* p_par = parent.buffer();
272 
273  for (unsigned i = 0; i < nelements; ++i)
274  {
275  if (*p_o == true)
276  {
277  if (*p_par == i)
278  *p_o = (*p_a > lambda);
279  else
280  *p_o = output.element(*p_par);
281  }
282  ++p_o;
283  ++p_a;
284  ++p_par;
285  }
286  }
287 
288 
289  return output;
290  }
291 
292  } // end of namespace scribo::primitive::extract::internal
293 
294 
295 
296  // FACADE
297 
298  template <typename L>
299  component_set<L>
300  non_text(const document<L>& doc, unsigned nlines)
301  {
302  mln_trace("scribo::primitive::extract::non_text");
303 
304 
306  t.start();
307 
308  mln_precondition(doc.is_valid());
309  mln_precondition(doc.has_text());
310 
311  // FIXME: Do these images exist elsewhere?
313  txt = make::text_components_image(doc),
314  txtblocks = make::text_blocks_image(doc, nlines);
315 
316  unsigned nbits = 5;
317  float p = 0.9998; // 0.80 <= x < 1.0
318  unsigned lambda = 1000;
319 
320  // enlarge the text mask so that "not txt" does not include
321  // any text pixel
322  txt = morpho::elementary::dilation(txt, c8());
323  txt = morpho::elementary::dilation(txt, c4());
324 
325  // FIXME: Make it faster?
326  data::fill((txtblocks | pw::value(txt)).rw(), false);
327 
328  // Debug
329  {
330  debug::logger().log_image(debug::AuxiliaryResults,
331  txt, "txt_components");
332  debug::logger().log_image(debug::AuxiliaryResults,
333  txtblocks, "txt_blocks");
334  }
335 
337  element_image = internal::learn(doc, txt, txtblocks, nbits, p);
338  element_image = internal::cleaning(element_image, lambda);
339 
340  mln_value(L) ncomps;
341  component_set<L>
342  elements = primitive::extract::components(element_image,
343  c8(), ncomps);
344 
345  // Debug
346  {
347  debug::logger().log_image(debug::Results,
348  elements.labeled_image(),
349  "non_text_components");
350  }
351 
352  return elements;
353  }
354 
355 # endif // ! MLN_INCLUDE_ONLY
356 
357 
358  } // end of namespace scribo::primitive::extract
359 
360  } // end of namespace scribo::primitive
361 
362 } // end of namespace scribo
363 
364 #endif // ! SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH