$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
line_info.hh
1 // Copyright (C) 2009, 2010, 2011, 2012, 2013 EPITA Research and Development
2 // Laboratory (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 
27 #ifndef SCRIBO_CORE_LINE_INFO_HH
28 # define SCRIBO_CORE_LINE_INFO_HH
29 
40 
41 # include <map>
42 
43 # include <mln/core/alias/box2d.hh>
44 # include <mln/core/alias/point2d.hh>
45 # include <mln/accu/stat/median_h.hh>
46 # include <mln/accu/stat/mean.hh>
47 # include <mln/accu/shape/bbox.hh>
48 # include <mln/math/min.hh>
49 # include <mln/util/object_id.hh>
50 # include <mln/value/int_u.hh>
51 # include <mln/math/sqr.hh>
52 # include <mln/value/rgb8.hh>
53 
54 # include <scribo/core/tag/component.hh>
55 # include <scribo/core/tag/line.hh>
56 
57 # include <scribo/core/object_groups.hh>
58 # include <scribo/core/line_set.hh>
59 # include <scribo/core/component_set.hh>
60 
61 # include <scribo/core/internal/sort_comp_ids.hh>
62 # include <scribo/core/concept/serializable.hh>
63 
64 # include <scribo/core/stats.hh>
65 
66 namespace scribo
67 {
68 
69  // Forward declarations.
70  template <typename L> class line_set;
71  template <typename L> class line_info;
72 
74 
75 
76  namespace internal
77  {
78 
80  template <typename L>
82  {
84  line_info_data(const line_set<L>& holder,
85  const group_info& group);
86  // Used for incremental construction (xml loading)
87  line_info_data(const line_set<L>& holder,
88  const mln::util::array<component_id_t>& component_ids);
89 
90  bool hidden_;
91  line::Tag tag_;
92  mln::box2d bbox_;
93  mln::box2d ebbox_;
94  mln::util::array<component_id_t> component_ids_;
95 
96  // The number of pixels used for line characters.
97  unsigned pixel_area_;
98 
99  // Values relative to the line bbox.
100  int baseline_;
101  int meanline_;
102 
103  // Values relative to the baseline.
104  unsigned x_height_;
105  int d_height_;
106  int a_height_;
107 
108  // Character related stats.
109  unsigned char_space_;
110  unsigned char_width_;
111 
112  // Words related stats.
113  unsigned word_space_;
114 
115  // Reading direction
116  line::ReadingDirection reading_direction_;
117 
118  // Line type
119  line::Type type_;
120 
121  // Is this line in reverse video?
122  bool reverse_video_;
123 
124  // Text orientation
125  float orientation_;
126 
127  // Text reading orientation
128  float reading_orientation_;
129 
130  bool indented_;
131 
132  float boldness_;
133  float boldness_reliability_;
134 
135  mln::value::rgb8 color_;
136 
137  // sqrt(Max(VAR(R), VAR(G), VAR(B)))
138  float color_reliability_;
139 
140  float text_confidence_;
141  std::string text_;
142  std::string html_text_;
143 
144  // DEBUG
145  stats< float > meanline_clusters_;
146  stats< float > baseline_clusters_;
147 
148  component_set<L> components_;
149  object_links<L> links_;
150 
151  private:
152  void init_();
153  };
154 
155  } // end of namespace scribo::internal
156 
157 
158 
159  template <typename L>
160  class line_info : public Serializable<line_info<L> >
161  {
166 
167  public:
168 
171 
172  line_info();
173 
174  line_info(const line_id_t& id, data_t* data);
175 
176  line_info(const line_set<L>& holder,
177  const line_id_t& id,
178  const group_info& group);
179 
181  line_info(const line_info<L>& other);
183 
185  line_info<L>& operator=(const line_info<L>& other);
186 
189  //
190  line_id_t id() const;
191 
192  line::Tag tag() const;
193  void update_tag(line::Tag tag);
194 
195  const mln::box2d& bbox() const;
196 
200  //
201  const mln::box2d& ebbox() const;
202 
203  const mln::util::array<component_id_t>& component_ids() const;
204  unsigned card() const;
205 
206  unsigned pixel_area() const;
207 
208  float boldness() const;
209  float boldness_reliability() const;
210 
211  const mln::value::rgb8& color() const;
212  float color_reliability() const;
213 
214  int baseline() const;
215  int meanline() const;
216  int ascent() const;
217  int descent() const;
218 
219  unsigned x_height() const;
220  int d_height() const;
221  int a_height() const;
222 
223  unsigned char_space() const;
224  unsigned char_width() const;
225 
226  unsigned word_space() const;
227 
228  line::ReadingDirection reading_direction() const;
229 
230  line::Type type() const;
231  void update_type(line::Type type);
232 
233  bool reverse_video() const;
234 
235  float orientation() const;
236  float reading_orientation() const;
237 
238  bool indented() const;
239 
240  bool has_text() const;
241  // Returns the percentage of confidence of the recognized text. If
242  // no text has been recognized, it returns -1.
243  float text_confidence() const;
244  const std::string& text() const;
245  const std::string& html_text() const;
246  void update_text(const std::string& str, float confidence = 100.0f);
247 
248  bool is_valid() const;
249 
250  bool is_textline() const;
251 
258  bool is_hidden() const;
259  void set_hidden(bool b);
261 
264 
272  //
273  void fast_merge(line_info<L>& other, bool hide = true);
274 
279  //
280  void precise_merge(line_info<L>& other, bool hide = true);
281 
283 
284  bool chars_same_width() const;
285  unsigned get_first_char_height() const;
287  void force_stats_update();
288 
290  int delta_of_line() const;
291 
293  void update_ebbox();
294 
295  private: // Members
296  void copy_data(const line_info<L>& other);
297 
299  mln::box2d enlarge(const mln::box2d& b, int delta) const;
300 
302  void update_bbox_and_ebox(line_info<L>& other);
303 
304  mln::box2d merged_ebbox(const scribo::line_info<L>& info_l,
305  const scribo::line_info<L>& info);
306 
307  void update_components_type(component::Type type);
308 
309  int compute_baseline();
310  int compute_meanline();
311 
312  private: // Attributes
313  line_id_t id_;
315  };
316 
317 
318  template <typename L>
319  std::ostream&
320  operator<<(std::ostream& ostr, const line_info<L>& info);
321 
322  template <typename L>
323  bool
324  operator==(const line_info<L>& lhs, const line_info<L>& rhs);
325 
326 
327 # ifndef MLN_INCLUDE_ONLY
328 
329  namespace internal
330  {
331 
332  // INTERNAL TOOLS
333 
334  static inline std::map<char, std::string> init_map()
335  {
336  std::map<char, std::string> html_map;
337  html_map['\"'] = "&quot;";
338  html_map['<'] = "&lt;";
339  html_map['>'] = "&gt;";
340  html_map['&'] = "&amp;";
341  return html_map;
342  }
343 
344 
345  inline
346  std::string
347  html_markups_replace(const std::string& input)
348  {
349  static std::map<char, std::string> map = init_map();
350 
351  std::string output = input;
352  for (unsigned i = 0; i < output.size(); ++i)
353  {
354  std::map<char, std::string>::iterator it = map.find(output.at(i));
355  if (it != map.end())
356  {
357  output.replace(i, 1, it->second);
358  i += it->second.size() - 1;
359  }
360  }
361 
362  return output;
363  }
364 
365 
366 
367  // LINE INFO DATA
368 
369  template <typename L>
370  line_info_data<L>::line_info_data()
371  {
372  hidden_ = false;
373  }
374 
375  template <typename L>
376  line_info_data<L>::line_info_data(const line_set<L>& holder,
377  const group_info& group)
378  : hidden_(false), tag_(line::None), component_ids_(group.component_ids()),
379  type_(line::Undefined), components_(holder.components()), links_(holder.links())
380  {
381  init_();
382  }
383 
384  template <typename L>
385  line_info_data<L>::line_info_data(const line_set<L>& holder,
386  const mln::util::array<component_id_t>& component_ids)
387  : hidden_(false), tag_(line::None), component_ids_(component_ids),
388  type_(line::Undefined), components_(holder.components()), links_(holder.links())
389  {
390  init_();
391  }
392 
393  template <typename L>
394  void
395  line_info_data<L>::init_()
396  {
397  // FIXME: set valid information for these attributes in
398  // force_stats_update.
399  word_space_ = 0;
400  reading_direction_ = line::LeftToRight;
401  reverse_video_ = false;
402 
403  orientation_ = 0.;
404  reading_orientation_ = 0.;
405 
406  indented_ = false;
407 
408  text_confidence_ = -1;
409  }
410 
411  } // end of namespace scribo::internal
412 
413 
414  template <typename L>
415  line_info<L>::line_info()
416  : parent_t(), id_(0)
417  {
418  }
419 
420  template <typename L>
421  inline
422  void
423  line_info<L>::copy_data(const line_info<L>& other)
424  {
425  // Id MUST NOT change except if this instance have no id.
426  if (! is_valid())
427  id_ = other.id();
428 
429  data_ = other.data_;
430  }
431 
432 
433  template <typename L>
434  inline
435  line_info<L>::line_info(const line_id_t& id, data_t *data)
436  : id_(id), data_(data)
437  {
438  }
439 
440 
441  template <typename L>
442  inline
443  line_info<L>::line_info(const line_info<L>& other)
444  : parent_t(other), id_(0)
445  {
446  //data_->hidden_ = false;
447  copy_data(other);
448  }
449 
450 
484  template <typename L>
485  line_info<L>::line_info(const line_set<L>& holder,
486  const line_id_t& id,
487  const group_info& group)
488  : id_(id)
489  {
490  data_ = new data_t(holder, group);
492  }
493 
494 
495  template <typename L>
496  inline
497  line_info<L>&
498  line_info<L>::operator=(const line_info<L>& other)
499  {
500  copy_data(other);
501  return *this;
502  }
503 
504  template <typename L>
505  typename line_info<L>::line_id_t
506  line_info<L>::id() const
507  {
508  return id_;
509  }
510 
511 
512  template <typename L>
513  line::Tag
514  line_info<L>::tag() const
515  {
516  return data_->tag_;
517  }
518 
519 
520  template <typename L>
521  void
522  line_info<L>::update_tag(line::Tag tag)
523  {
524  data_->tag_ = tag;
525  }
526 
527 
528  template <typename L>
529  const mln::box2d&
530  line_info<L>::bbox() const
531  {
532  return data_->bbox_;
533  }
534 
535  template <typename L>
536  const mln::box2d&
537  line_info<L>::ebbox() const
538  {
539  return data_->ebbox_;
540  }
541 
542 
543  template <typename L>
545  line_info<L>::component_ids() const
546  {
547  return data_->component_ids_;
548  }
549 
550 
551  template <typename L>
552  unsigned
553  line_info<L>::card() const
554  {
555  return data_->component_ids_.size();
556  }
557 
558 
559  template <typename L>
560  unsigned
561  line_info<L>::pixel_area() const
562  {
563  return data_->pixel_area_;
564  }
565 
566 
567  template <typename L>
568  float
569  line_info<L>::boldness() const
570  {
571  return data_->boldness_;
572  }
573 
574 
575  template <typename L>
576  float
577  line_info<L>::boldness_reliability() const
578  {
579  return data_->boldness_reliability_;
580  }
581 
582 
583  template <typename L>
584  const mln::value::rgb8&
585  line_info<L>::color() const
586  {
587  return data_->color_;
588  }
589 
590 
591  template <typename L>
592  float
593  line_info<L>::color_reliability() const
594  {
595  return data_->color_reliability_;
596  }
597 
598 
599  template <typename L>
600  int
601  line_info<L>::baseline() const
602  {
603  return data_->baseline_;
604  }
605 
606 
607  template <typename L>
608  int
609  line_info<L>::meanline() const
610  {
611  return data_->meanline_;
612  }
613 
614 
615  template <typename L>
616  int
617  line_info<L>::ascent() const
618  {
619  return data_->baseline_ - a_height() + 1;
620  }
621 
622 
623  template <typename L>
624  int
625  line_info<L>::descent() const
626  {
627  return data_->baseline_ - d_height();
628  }
629 
630 
631  template <typename L>
632  unsigned
633  line_info<L>::x_height() const
634  {
635  return data_->x_height_;
636  }
637 
638 
639  template <typename L>
640  int
641  line_info<L>::d_height() const
642  {
643  return data_->d_height_;
644  }
645 
646 
647  template <typename L>
648  int
649  line_info<L>::a_height() const
650  {
651  return data_->a_height_;
652  }
653 
654 
655  template <typename L>
656  unsigned
657  line_info<L>::char_space() const
658  {
659  return data_->char_space_;
660  }
661 
662 
663  template <typename L>
664  unsigned
665  line_info<L>::char_width() const
666  {
667  return data_->char_width_;
668  }
669 
670 
671  template <typename L>
672  unsigned
673  line_info<L>::word_space() const
674  {
675  return data_->word_space_;
676  }
677 
678 
679  template <typename L>
680  line::ReadingDirection
681  line_info<L>::reading_direction() const
682  {
683  return data_->reading_direction_;
684  }
685 
686  template <typename L>
687  line::Type
688  line_info<L>::type() const
689  {
690  return data_->type_;
691  }
692 
693 
694  template <typename L>
695  void
696  line_info<L>::update_components_type(component::Type type)
697  {
698  for_all_elements(i, data_->component_ids_)
699  {
700  unsigned c = data_->component_ids_[i];
701  data_->components_(c).update_type(type);
702  }
703  }
704 
705 
706  template <typename L>
707  void
708  line_info<L>::update_type(line::Type type)
709  {
710  data_->type_ = type;
711 
712  // Some line types may involve updating components type as well.
713  if (type == line::Punctuation)
714  update_components_type(component::Punctuation);
715  else if (type == line::Text)
716  update_components_type(component::Character);
717  }
718 
719 
720  template <typename L>
721  bool
722  line_info<L>::reverse_video() const
723  {
724  return data_->reverse_video_;
725  }
726 
727 
728  template <typename L>
729  float
730  line_info<L>::orientation() const
731  {
732  return data_->orientation_;
733  }
734 
735 
736  template <typename L>
737  float
738  line_info<L>::reading_orientation() const
739  {
740  return data_->reading_orientation_;
741  }
742 
743 
744  template <typename L>
745  bool
746  line_info<L>::indented() const
747  {
748  return data_->indented_;
749  }
750 
751 
752  template <typename L>
753  bool
754  line_info<L>::has_text() const
755  {
756  return !data_->text_.empty();
757  }
758 
759 
760  template <typename L>
761  float
762  line_info<L>::text_confidence() const
763  {
764  return data_->text_confidence_;
765  }
766 
767 
768  template <typename L>
769  const std::string&
770  line_info<L>::text() const
771  {
772  return data_->text_;
773  }
774 
775 
776  template <typename L>
777  const std::string&
778  line_info<L>::html_text() const
779  {
780  return data_->html_text_;
781  }
782 
783 
784  template <typename L>
785  void
786  line_info<L>::update_text(const std::string& str, float confidence)
787  {
788  data_->text_confidence_ = confidence;
789  data_->text_ = str;
790  data_->html_text_ = scribo::internal::html_markups_replace(str);
791  }
792 
793 
794  template <typename L>
795  bool
796  line_info<L>::is_valid() const
797  {
798  return id_ != 0u;
799  }
800 
801 
802  template <typename L>
803  bool
804  line_info<L>::is_textline() const
805  {
806  return is_valid()
807  && !is_hidden()
808  && type() == line::Text;
809  }
810 
811 
812  template <typename L>
813  bool
815  {
816  return data_->hidden_;
817  }
818 
819 
820  template <typename L>
821  void
823  {
824  data_->hidden_ = b;
825  }
826 
827 
828  template <typename L>
829  inline
830  int
832  {
833  return 2 * char_width() + 2 * char_space();
834  // FIXME: choose between:
835  // not enough: char_width + char_space
836  // too much: 2 * char_width
837  // looks good: char_width + 2 * char_space
838  }
839 
840 
841  template <typename L>
842  mln::box2d
843  line_info<L>::enlarge(const mln::box2d& b, int delta) const
844  {
845  mln::box2d b_(mln::point2d(b.pmin().row(), b.pmin().col() - delta),
846  mln::point2d(b.pmax().row(), b.pmax().col() + delta));
847  return b_;
848  }
849 
850 
851  template <typename L>
852  void
854  {
855  int A = data_->a_height_ - data_->x_height_;
856  int D = - data_->d_height_;
857  if (A <= 2 && D > 2)
858  A = D;
859  if (D <= 2 && A > 2)
860  D = A;
861 
862  int delta = delta_of_line();
863 
864  data_->ebbox_ = mln::make::box2d(data_->meanline_ - A,
865  bbox().pmin().col() - delta,
866  data_->baseline_ + D,
867  bbox().pmax().col() + delta);
868 
869  data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
870  }
871 
872 
873  template <typename L>
874  mln::box2d
875  line_info<L>::merged_ebbox(const scribo::line_info<L>& info_l,
876  const scribo::line_info<L>& info)
877  {
878  // line data
879  int
880  baseline_l = info_l.baseline(),
881  d_height = info_l.d_height();
882  unsigned
883  a_height = info_l.a_height(),
884  x_height = info_l.x_height();
885  int A_l = a_height - x_height;
886  int D_l = - d_height;
887  if (A_l <= 2 && D_l > 2)
888  A_l = D_l;
889  if (D_l <= 2 && A_l > 2)
890  D_l = A_l;
891  unsigned delta_l = info_l.delta_of_line();
892  int meanline_l = info_l.meanline();
893 
894  // non-line data
895  unsigned delta_ = info.delta_of_line();
896 
897  mln::box2d b = mln::make::box2d(// pmin
898  meanline_l - A_l,
899  std::min(info_l.bbox().pmin().col(), info.bbox().pmin().col()) - std::max(delta_l, delta_),
900  // pmax
901  baseline_l + D_l,
902  std::max(info_l.bbox().pmax().col(), info.bbox().pmax().col()) + std::max(delta_l, delta_));
903 
904  return b;
905  }
906 
907 
908  template <typename L>
909  void
910  line_info<L>::update_bbox_and_ebox(line_info<L>& other)
911  {
912  // Merging ebboxes depending on the type of the line.
913 
914  if (type() == line::Text) // /this/ IS a text line
915  {
916  if (other.type() == line::Text) // /other/ IS a text line.
917  {
918  // Adjusting ebboxes with the highest delta and merging ebboxes.
919  int d_delta = other.delta_of_line() - this->delta_of_line();
920  if (d_delta < 0) // other.delta_of_line() < this->delta_of_line()
921  data_->ebbox_.merge(enlarge(other.ebbox(), - d_delta));
922  else
923  {
924  mln::box2d b = data_->ebbox_;
925  data_->ebbox_ = other.bbox();
926  data_->ebbox_.merge(enlarge(b, d_delta));
927  }
928 
929  data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
930  }
931  else // /other/ IS NOT a text line.
932  {
933  data_->ebbox_.merge(other.ebbox());
934  data_->ebbox_.merge(merged_ebbox(*this, other));
935  }
936  }
937  else // /this/ is NOT a text line
938  {
939  if (other.type() != line::Text)
940  {
941  std::cerr << "error in 'line_info::update_bbox_and_ebox':"
942  << "Merging two non text lines." << std::endl;
943  std::abort();
944  }
945 
946  update_type(line::Text);
947  data_->ebbox_.merge(other.ebbox());
948  data_->ebbox_.merge(merged_ebbox(other, *this));
949  }
950 
951  // Merging bboxes.
952  data_->bbox_.merge(other.bbox());
953 
954  // Make sure the ebbox is included in the image domain.
955  data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
956  }
957 
958 
959  template <typename L>
960  void
961  line_info<L>::fast_merge(line_info<L>& other, bool hide)
962  {
963  data_->tag_ = line::Needs_Precise_Stats_Update;
964  other.update_tag(line::Merged);
965  other.set_hidden(hide);
966 
967  // Update bbox and ebbox
968  update_bbox_and_ebox(other);
969 
970  data_->component_ids_.append(other.component_ids());
971  }
972 
973 
974  template <typename L>
975  void
976  line_info<L>::precise_merge(line_info<L>& other, bool hide)
977  {
978  fast_merge(other, hide);
979  force_stats_update();
980  }
981 
982  template <typename L>
983  bool
984  line_info<L>::chars_same_width() const
985  {
986  // Only for the case of two-character words
987  if (card() == 2)
988  {
989  const component_set<L>& comp_set = data_->components_;
990 
991  const unsigned c1 = data_->component_ids_(0);
992  const unsigned c2 = data_->component_ids_(1);
993 
994  if (data_->components_(c1).type() == component::Punctuation
995  || data_->components_(c2).type() == component::Punctuation)
996  return false;
997 
998  const mln::box2d& bb1 = comp_set(c1).bbox();
999  const mln::box2d& bb2 = comp_set(c2).bbox();
1000 
1001  const float w1 = bb1.width();
1002  const float h1 = bb1.height();
1003  const float w2 = bb2.width();
1004  const float h2 = bb2.height();
1005 
1006  const float space = std::max(bb1.pmin().col(), bb2.pmin().col()) -
1007  std::min(bb1.pmax().col(), bb2.pmax().col());
1008 
1009  const int dy = bb1.pmax().row() - bb2.pmax().row();
1010 
1011  // The two characters must be distinct
1012  // if (space < 0)
1013  // return false;
1014 
1015  if (// Approximately the same width
1016  ((std::max(w1, w2) / std::min(w1, w2)) > 1.3 ||
1017  // One character must not be smaller than the space between
1018  // the two characters
1019  (w1 < space || w2 < space))
1020  // If the two characters have a different width they must also
1021  // have a different height
1022  && not (std::max(h1, h2) / std::min(h1, h2) <= 1.7f))
1023  return false;
1024 
1025  // Approximately aligned on baseline
1026  if (std::abs(dy) > 10 &&
1027  not (std::max(h1, h2) / std::min(h1, h2) <= 1.7f))
1028  return false;
1029 
1030  return true;
1031  }
1032 
1033  return false;
1034  }
1035 
1036  template< typename L >
1037  unsigned
1038  line_info<L>::get_first_char_height() const
1039  {
1040  const component_set<L>& comp_set = data_->components_;
1041  const unsigned c1 = data_->components_(0);
1042  const mln::box2d& bb1 = comp_set(c1).bbox();
1043 
1044  return bb1.height();
1045  }
1046 
1047  template <typename L>
1048  int
1049  line_info<L>::compute_baseline()
1050  {
1051  const unsigned nelements = data_->baseline_clusters_.nelements();
1052 
1053  if (nelements == 2)
1054  return data_->baseline_clusters_.mean();
1055 
1056  mln::util::array< cluster_stats< float > >& clusters_b = data_->baseline_clusters_.clusters();
1057 
1058  unsigned index = 0;
1059  float min_base = 0.0f;
1060  const unsigned clusters_b_nelements = clusters_b.nelements();
1061 
1062  if (clusters_b_nelements >= 3)
1063  return data_->baseline_clusters_.mean();
1064 
1065 
1066  for (unsigned i = 0; i < clusters_b_nelements; ++i)
1067  {
1068  const unsigned clusters_b_i_nelements = clusters_b[i].nelements();
1069 
1070  if (clusters_b_i_nelements >= min_base * 2.0f)
1071  {
1072  min_base = clusters_b_i_nelements;
1073  index = i;
1074  }
1075  else if (clusters_b_i_nelements >= 0.5f * min_base)
1076  {
1077  if (clusters_b_i_nelements > 1 &&
1078  clusters_b[index].median() > clusters_b[i].median())
1079  {
1080  if (clusters_b_i_nelements > min_base)
1081  min_base = clusters_b_i_nelements;
1082  index = i;
1083  }
1084  }
1085  }
1086 
1087  if (clusters_b[index].nelements() <= 2 && nelements <= 5)
1088  return data_->baseline_clusters_.mean();
1089 
1090  return clusters_b[index].median();
1091  }
1092 
1093  template <typename L>
1094  int
1095  line_info<L>::compute_meanline()
1096  {
1097  mln::util::array< cluster_stats< float > >& clusters_m = data_->meanline_clusters_.clusters();
1098 
1099  unsigned index = 0;
1100  float max_mean = 0.0f;
1101  const unsigned clusters_m_nelements = clusters_m.nelements();
1102 
1103  if (clusters_m_nelements >= 3)
1104  return data_->meanline_clusters_.mean();
1105 
1106  for (unsigned i = 0; i < clusters_m_nelements; ++i)
1107  {
1108  const unsigned clusters_m_i_nelements = clusters_m[i].nelements();
1109 
1110  if (clusters_m_i_nelements >= max_mean * 2.0f)
1111  {
1112  max_mean = clusters_m_i_nelements;
1113  index = i;
1114  }
1115  else if (clusters_m_i_nelements >= 0.5f * max_mean)
1116  {
1117  if (clusters_m[index].median() < clusters_m[i].median())
1118  {
1119  if (clusters_m_i_nelements > max_mean)
1120  max_mean = clusters_m_i_nelements;
1121  index = i;
1122  }
1123  }
1124  }
1125 
1126  return clusters_m[index].median();
1127  }
1128 
1129  template <typename L>
1130  void
1132  {
1133  typedef mln_site(L) P;
1134  const component_set<L>& comp_set = data_->components_;
1135 
1136  // Init.
1137  typedef mln::value::int_u<12> median_data_t;
1138  typedef mln::accu::stat::median_h<median_data_t> median_t;
1139  median_t
1140  // meanline,
1141  // baseline,
1142  char_space,
1143  char_width;
1144 
1145  // FIXME: we should use the median for computing the line
1146  // color. Means should be kept for variance computation.
1147  mln::accu::stat::mean<mln::value::int_u<8> >
1148  color_red,
1149  color_green,
1150  color_blue,
1151  boldness;
1152 
1153  float
1154  sum2_red = 0,
1155  sum2_green = 0,
1156  sum2_blue = 0,
1157  sum2_boldness = 0;
1158 
1159  unsigned pixel_area = 0;
1160 
1161  mln::accu::shape::bbox<P> bbox;
1162 
1163  mln::def::coord ref_line = mln_max(mln::def::coord);
1164 
1165  // DEBUG
1166  data_->baseline_clusters_.reset();
1167  data_->meanline_clusters_.reset();
1168 
1169  // Find a reference line to compute baselines and other attributes.
1170  // Workaround to avoid overflow with int_u<12> in median accumulators.
1171  //
1172  // FIXME: not optimal...
1173  for_all_elements(i, data_->component_ids_)
1174  {
1175  unsigned c = data_->component_ids_(i);
1176 
1177  // Ignore punctuation for stats computation but not for bbox
1178  // computation.
1179  if (comp_set(c).type() == component::Punctuation)
1180  continue;
1181 
1182  ref_line = mln::math::min(comp_set(c).bbox().pmin().row(), ref_line);
1183  }
1184 
1185 
1186  unsigned used_comps = 0;
1187  for_all_elements(i, data_->component_ids_)
1188  {
1189  unsigned c = data_->component_ids_(i);
1190 
1191  pixel_area += comp_set(c).card();
1192 
1193  const mln::box2d& bb = comp_set(c).bbox();
1194 
1195  // Bounding box.
1196  bbox.take(bb);
1197 
1198  // Ignore punctuation for stats computation but not for bbox
1199  // computation.
1200  if (comp_set(c).type() == component::Punctuation)
1201  continue;
1202 
1203  // This component will be used for stats, the counter is
1204  // incremented.
1205  ++used_comps;
1206 
1207 
1208  // COMPUTE FEATURES DATA
1209  if (comp_set(c).has_features())
1210  {
1211  // Compute boldness
1212  boldness.take(comp_set(c).features().boldness);
1213  sum2_boldness += mln::math::sqr<float>(comp_set(c).features().boldness);
1214 
1215  // Compute color
1216  color_red.take(comp_set(c).features().color.red());
1217  color_green.take(comp_set(c).features().color.green());
1218  color_blue.take(comp_set(c).features().color.blue());
1219 
1220  sum2_red += mln::math::sqr<unsigned>(comp_set(c).features().color.red());
1221  sum2_green += mln::math::sqr<unsigned>(comp_set(c).features().color.green());
1222  sum2_blue += mln::math::sqr<unsigned>(comp_set(c).features().color.blue());
1223  }
1224 
1225  // FIXME: we must guaranty here that the relationship is from
1226  // right to left, otherwise, the space size computed between
1227  // boxes might be wrong...
1228  //
1229  // x------| |-| != |------| x-|
1230  // |------| x-| |------x |-|
1231  //
1232  // (incorrect) (correct)
1233  // (right link) (left link)
1234 
1235  // Space between characters.
1236  if (data_->links_(c) != c)
1237  {
1238  int
1239  space = bb.pmin().col()
1240  - comp_set(data_->links_(c)).bbox().pmax().col() - 1;
1241 
1242  // -- Ignore overlapped characters.
1243  if (space > 0)
1244  char_space.take(space);
1245  }
1246 
1247  // Character width
1248  // -- Ignore too large components.
1249  //
1250  // FIXME: should not be a constant?
1251  if (bb.width() <= 1000)
1252  char_width.take(bb.width());
1253 
1254  // Meanline (compute an absolute value, from the top left corner
1255  // of the highest character bounding box, excluding
1256  // punctuation).
1257  // meanline.take(bb.pmin().row() - ref_line);
1258  data_->meanline_clusters_.take(bb.pmin().row());
1259 
1260  // Baseline (compute an absolute value, from the top left corner
1261  // of the highest character bounding box, excluding
1262  // punctuation).
1263  // baseline.take(bb.pmax().row() - ref_line);
1264  data_->baseline_clusters_.take(bb.pmax().row());
1265  }
1266 
1267  // Finalization
1268  {
1269  // Tag
1270  data_->tag_ = line::None;
1271 
1272  // Bbox
1273  data_->bbox_ = bbox.to_result();
1274 
1275  // Pixel area
1276  data_->pixel_area_ = pixel_area;
1277 
1278  // Order component ids according to component localization (left
1279  // to right).
1280  std::sort(data_->component_ids_.hook_std_vector_().begin(),
1281  data_->component_ids_.hook_std_vector_().end(),
1282  internal::sort_comp_ids<L>(comp_set));
1283 
1284  // Boldness
1285  data_->boldness_ = boldness.to_result();
1286  data_->boldness_reliability_ = std::sqrt(sum2_boldness);
1287 
1288  // Color
1289  data_->color_ = mln::value::rgb8(color_red, color_green, color_blue);
1290 
1291  // FIXME: we may prefer using the non-biased variance.
1292  float
1293  var_red = sum2_red / (float)(used_comps)
1294  - mln::math::sqr<float>(color_red.to_result()),
1295  var_green = sum2_green / (float)(used_comps)
1296  - mln::math::sqr<float>(color_green.to_result()),
1297  var_blue = sum2_blue / (float)(used_comps)
1298  - mln::math::sqr<float>(color_blue.to_result());
1299 
1300  data_->color_reliability_ = std::sqrt(std::max(var_red,
1301  std::max(var_green, var_blue)));
1302 
1303  // Char space
1304  if (char_space.card() < 2)
1305  data_->char_space_ = 0;
1306  else
1307  data_->char_space_ = char_space.to_result();
1308 
1309  // Char width
1310  if (card() == 2)
1311  data_->char_width_ = (comp_set(data_->component_ids_[0]).bbox().width()
1312  + comp_set(data_->component_ids_[1]).bbox().width()) / 2;
1313  else
1314  data_->char_width_ = char_width.to_result();
1315 
1316  // // mln::def::coord
1317  // // absolute_baseline_r = baseline.to_result() + ref_line,
1318  // // absolute_meanline_r = meanline.to_result() + ref_line;
1319 
1320  // data_->baseline_ = absolute_baseline_r;
1321  // data_->meanline_ = absolute_meanline_r;
1322  data_->baseline_ = compute_baseline();
1323  data_->meanline_ = compute_meanline();
1324  data_->x_height_ = data_->baseline_ - data_->meanline_ + 1;
1325  data_->d_height_ = data_->baseline_ - bbox.to_result().pmax().row();
1326  data_->a_height_ = data_->baseline_ - bbox.to_result().pmin().row() + 1;
1327 
1328  //FIXME
1329  //
1330  //word_space_ = ...;
1331  //reading_direction_ = ...;
1332  //reverse_video_ = ...;
1333  //orientation_ = ...;
1334  //reading_orientation_ = ...;
1335  //indented_ = ...;
1336 
1337  update_ebbox();
1338  }
1339 
1340  }
1341 
1342 
1343  template <typename L>
1344  std::ostream&
1345  operator<<(std::ostream& ostr, const line_info<L>& info)
1346  {
1347  return ostr << "line_info("
1348  << "id=" << info.id()
1349  << ", tag=" << info.tag()
1350  << ", type=" << info.type()
1351  << ", bbox=" << info.bbox()
1352  << ", ebbox=" << info.ebbox()
1353  << ", boldness=" << info.boldness()
1354  << ", boldness_reliability=" << info.boldness_reliability()
1355  << ", color=" << info.color()
1356  << ", color_reliability=" << info.color_reliability()
1357  << ", components=" << info.component_ids()
1358  << ", baseline=" << info.baseline()
1359  << ", meanline=" << info.meanline()
1360  << ", ascent=" << info.ascent()
1361  << ", descent=" << info.descent()
1362  << ", x_height=" << info.x_height()
1363  << ", d_height=" << info.d_height()
1364  << ", a_height=" << info.a_height()
1365  << ", char_space=" << info.char_space()
1366  << ", char_width=" << info.char_width()
1367  << ", word_space=" << info.word_space()
1368  << ", reading_direction=" << info.reading_direction()
1369  << ", type=" << info.type()
1370  << ", reverse_video=" << info.reverse_video()
1371  << ", orientation=" << info.orientation()
1372  << ", reading_orientation=" << info.reading_orientation()
1373  << ", indented=" << info.indented()
1374  << ", hidden=" << info.is_hidden()
1375  << ", text=" << info.text()
1376  << ", html_text=" << info.html_text()
1377  << ")" << std::endl;
1378  }
1379 
1380 
1381  template <typename L>
1382  bool
1383  operator==(const line_info<L>& lhs, const line_info<L>& rhs)
1384  {
1385  if (! lhs.is_valid() && ! rhs.is_valid())
1386  return true;
1387 
1388  return
1389  lhs.is_valid() == rhs.is_valid()
1390  && lhs.id() == rhs.id()
1391  && lhs.pixel_area() == rhs.pixel_area()
1392  && lhs.tag() == rhs.tag()
1393  && lhs.type() == rhs.type()
1394  && lhs.bbox() == rhs.bbox()
1395  && lhs.ebbox() == rhs.ebbox()
1396  && lhs.boldness() == rhs.boldness()
1397  && lhs.boldness_reliability() == rhs.boldness_reliability()
1398  && lhs.color() == rhs.color()
1399  && lhs.color_reliability() == rhs.color_reliability()
1400  && lhs.component_ids() == rhs.component_ids()
1401  && lhs.baseline() == rhs.baseline()
1402  && lhs.meanline() == rhs.meanline()
1403  && lhs.ascent() == rhs.ascent()
1404  && lhs.descent() == rhs.descent()
1405  && lhs.x_height() == rhs.x_height()
1406  && lhs.d_height() == rhs.d_height()
1407  && lhs.a_height() == rhs.a_height()
1408  && lhs.char_space() == rhs.char_space()
1409  && lhs.char_width() == rhs.char_width()
1410  && lhs.word_space() == rhs.word_space()
1411  && lhs.reading_orientation() == rhs.reading_orientation()
1412  && lhs.type() == rhs.type()
1413  && lhs.reverse_video() == rhs.reverse_video()
1414  && lhs.orientation() == rhs.orientation()
1415  && lhs.reading_orientation() == rhs.reading_orientation()
1416  && lhs.indented() == rhs.indented()
1417  && lhs.is_hidden() == rhs.is_hidden()
1418  && lhs.text() == rhs.text()
1419  && lhs.html_text() == rhs.html_text();
1420  }
1421 
1422 # endif// ! MLN_INCLUDE_ONLY
1423 
1424 
1425 } // end of namespace scribo
1426 
1427 
1428 #endif // ! SCRIBO_CORE_LINE_INFO_HH