27 #ifndef SCRIBO_CORE_LINE_INFO_HH
28 # define SCRIBO_CORE_LINE_INFO_HH
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>
54 # include <scribo/core/tag/component.hh>
55 # include <scribo/core/tag/line.hh>
57 # include <scribo/core/object_groups.hh>
58 # include <scribo/core/line_set.hh>
59 # include <scribo/core/component_set.hh>
61 # include <scribo/core/internal/sort_comp_ids.hh>
62 # include <scribo/core/concept/serializable.hh>
64 # include <scribo/core/stats.hh>
70 template <
typename L>
class line_set;
71 template <
typename L>
class line_info;
109 unsigned char_space_;
110 unsigned char_width_;
113 unsigned word_space_;
116 line::ReadingDirection reading_direction_;
128 float reading_orientation_;
133 float boldness_reliability_;
138 float color_reliability_;
140 float text_confidence_;
142 std::string html_text_;
159 template <
typename L>
192 line::Tag tag()
const;
193 void update_tag(line::Tag tag);
204 unsigned card()
const;
206 unsigned pixel_area()
const;
208 float boldness()
const;
209 float boldness_reliability()
const;
212 float color_reliability()
const;
214 int baseline()
const;
215 int meanline()
const;
219 unsigned x_height()
const;
220 int d_height()
const;
221 int a_height()
const;
223 unsigned char_space()
const;
224 unsigned char_width()
const;
226 unsigned word_space()
const;
228 line::ReadingDirection reading_direction()
const;
230 line::Type type()
const;
231 void update_type(line::Type type);
233 bool reverse_video()
const;
235 float orientation()
const;
236 float reading_orientation()
const;
238 bool indented()
const;
240 bool has_text()
const;
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);
248 bool is_valid()
const;
250 bool is_textline()
const;
284 bool chars_same_width()
const;
285 unsigned get_first_char_height()
const;
309 int compute_baseline();
310 int compute_meanline();
318 template <
typename L>
320 operator<<(std::ostream& ostr, const line_info<L>&
info);
322 template <
typename L>
327 # ifndef MLN_INCLUDE_ONLY
334 static inline std::map<char, std::string> init_map()
336 std::map<char, std::string> html_map;
337 html_map[
'\"'] =
""";
338 html_map[
'<'] =
"<";
339 html_map[
'>'] =
">";
340 html_map[
'&'] =
"&";
347 html_markups_replace(
const std::string& input)
349 static std::map<char, std::string> map = init_map();
351 std::string output = input;
352 for (
unsigned i = 0; i < output.size(); ++i)
354 std::map<char, std::string>::iterator it = map.find(output.at(i));
357 output.replace(i, 1, it->second);
358 i += it->second.size() - 1;
369 template <
typename L>
370 line_info_data<L>::line_info_data()
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()),
384 template <
typename L>
385 line_info_data<L>::line_info_data(
const line_set<L>& holder,
387 : hidden_(false), tag_(
line::None), component_ids_(component_ids),
393 template <
typename L>
395 line_info_data<L>::init_()
400 reading_direction_ = line::LeftToRight;
401 reverse_video_ =
false;
404 reading_orientation_ = 0.;
408 text_confidence_ = -1;
414 template <
typename L>
415 line_info<L>::line_info()
420 template <
typename L>
423 line_info<L>::copy_data(
const line_info<L>& other)
433 template <
typename L>
436 : id_(id), data_(data)
441 template <
typename L>
444 : parent_t(other), id_(0)
484 template <
typename L>
487 const group_info& group)
490 data_ =
new data_t(holder, group);
495 template <
typename L>
504 template <
typename L>
505 typename line_info<L>::line_id_t
512 template <
typename L>
514 line_info<L>::tag()
const
520 template <
typename L>
522 line_info<L>::update_tag(line::Tag tag)
528 template <
typename L>
535 template <
typename L>
539 return data_->ebbox_;
543 template <
typename L>
545 line_info<L>::component_ids()
const
547 return data_->component_ids_;
551 template <
typename L>
553 line_info<L>::card()
const
555 return data_->component_ids_.
size();
559 template <
typename L>
561 line_info<L>::pixel_area()
const
563 return data_->pixel_area_;
567 template <
typename L>
569 line_info<L>::boldness()
const
571 return data_->boldness_;
575 template <
typename L>
577 line_info<L>::boldness_reliability()
const
579 return data_->boldness_reliability_;
583 template <
typename L>
585 line_info<L>::color()
const
587 return data_->color_;
591 template <
typename L>
593 line_info<L>::color_reliability()
const
595 return data_->color_reliability_;
599 template <
typename L>
601 line_info<L>::baseline()
const
603 return data_->baseline_;
607 template <
typename L>
609 line_info<L>::meanline()
const
611 return data_->meanline_;
615 template <
typename L>
617 line_info<L>::ascent()
const
619 return data_->baseline_ - a_height() + 1;
623 template <
typename L>
625 line_info<L>::descent()
const
627 return data_->baseline_ - d_height();
631 template <
typename L>
633 line_info<L>::x_height()
const
635 return data_->x_height_;
639 template <
typename L>
641 line_info<L>::d_height()
const
643 return data_->d_height_;
647 template <
typename L>
649 line_info<L>::a_height()
const
651 return data_->a_height_;
655 template <
typename L>
657 line_info<L>::char_space()
const
659 return data_->char_space_;
663 template <
typename L>
665 line_info<L>::char_width()
const
667 return data_->char_width_;
671 template <
typename L>
673 line_info<L>::word_space()
const
675 return data_->word_space_;
679 template <
typename L>
680 line::ReadingDirection
681 line_info<L>::reading_direction()
const
683 return data_->reading_direction_;
686 template <
typename L>
688 line_info<L>::type()
const
694 template <
typename L>
698 for_all_elements(i, data_->component_ids_)
700 unsigned c = data_->component_ids_[i];
701 data_->components_(c).update_type(type);
706 template <
typename L>
708 line_info<L>::update_type(line::Type type)
713 if (type == line::Punctuation)
715 else if (type == line::Text)
720 template <
typename L>
722 line_info<L>::reverse_video()
const
724 return data_->reverse_video_;
728 template <
typename L>
730 line_info<L>::orientation()
const
732 return data_->orientation_;
736 template <
typename L>
738 line_info<L>::reading_orientation()
const
740 return data_->reading_orientation_;
744 template <
typename L>
746 line_info<L>::indented()
const
748 return data_->indented_;
752 template <
typename L>
754 line_info<L>::has_text()
const
756 return !data_->text_.empty();
760 template <
typename L>
762 line_info<L>::text_confidence()
const
764 return data_->text_confidence_;
768 template <
typename L>
770 line_info<L>::text()
const
776 template <
typename L>
778 line_info<L>::html_text()
const
780 return data_->html_text_;
784 template <
typename L>
786 line_info<L>::update_text(
const std::string& str,
float confidence)
788 data_->text_confidence_ = confidence;
790 data_->html_text_ = scribo::internal::html_markups_replace(str);
794 template <
typename L>
796 line_info<L>::is_valid()
const
802 template <
typename L>
804 line_info<L>::is_textline()
const
808 && type() == line::Text;
812 template <
typename L>
816 return data_->hidden_;
820 template <
typename L>
828 template <
typename L>
833 return 2 * char_width() + 2 * char_space();
841 template <
typename L>
851 template <
typename L>
855 int A = data_->a_height_ - data_->x_height_;
856 int D = - data_->d_height_;
862 int delta = delta_of_line();
865 bbox().pmin().col() - delta,
866 data_->baseline_ + D,
867 bbox().pmax().col() + delta);
869 data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
873 template <
typename L>
880 baseline_l = info_l.baseline(),
881 d_height = info_l.d_height();
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)
889 if (D_l <= 2 && A_l > 2)
892 int meanline_l = info_l.meanline();
908 template <
typename L>
910 line_info<L>::update_bbox_and_ebox(line_info<L>& other)
914 if (type() == line::Text)
916 if (other.type() == line::Text)
919 int d_delta = other.delta_of_line() - this->delta_of_line();
921 data_->ebbox_.merge(
enlarge(other.ebbox(), - d_delta));
925 data_->ebbox_ = other.
bbox();
926 data_->ebbox_.merge(
enlarge(b, d_delta));
929 data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
933 data_->ebbox_.merge(other.ebbox());
934 data_->ebbox_.merge(merged_ebbox(*
this, other));
939 if (other.type() != line::Text)
941 std::cerr <<
"error in 'line_info::update_bbox_and_ebox':"
942 <<
"Merging two non text lines." << std::endl;
946 update_type(line::Text);
947 data_->ebbox_.merge(other.ebbox());
948 data_->ebbox_.merge(merged_ebbox(other, *
this));
952 data_->bbox_.merge(other.bbox());
955 data_->ebbox_.crop_wrt(data_->components_.labeled_image().domain());
959 template <
typename L>
963 data_->tag_ = line::Needs_Precise_Stats_Update;
964 other.update_tag(line::Merged);
965 other.set_hidden(hide);
968 update_bbox_and_ebox(other);
970 data_->component_ids_.append(other.component_ids());
974 template <
typename L>
978 fast_merge(other, hide);
979 force_stats_update();
982 template <
typename L>
984 line_info<L>::chars_same_width()
const
989 const component_set<L>& comp_set = data_->components_;
991 const unsigned c1 = data_->component_ids_(0);
992 const unsigned c2 = data_->component_ids_(1);
1001 const float w1 = bb1.width();
1002 const float h1 = bb1.height();
1003 const float w2 = bb2.width();
1004 const float h2 = bb2.height();
1019 (w1 < space || w2 < space))
1036 template<
typename L >
1038 line_info<L>::get_first_char_height()
const
1040 const component_set<L>& comp_set = data_->components_;
1041 const unsigned c1 = data_->components_(0);
1044 return bb1.height();
1047 template <
typename L>
1049 line_info<L>::compute_baseline()
1051 const unsigned nelements = data_->baseline_clusters_.nelements();
1054 return data_->baseline_clusters_.mean();
1059 float min_base = 0.0f;
1060 const unsigned clusters_b_nelements = clusters_b.
nelements();
1062 if (clusters_b_nelements >= 3)
1063 return data_->baseline_clusters_.mean();
1066 for (
unsigned i = 0; i < clusters_b_nelements; ++i)
1068 const unsigned clusters_b_i_nelements = clusters_b[i].
nelements();
1070 if (clusters_b_i_nelements >= min_base * 2.0f)
1072 min_base = clusters_b_i_nelements;
1075 else if (clusters_b_i_nelements >= 0.5f * min_base)
1077 if (clusters_b_i_nelements > 1 &&
1080 if (clusters_b_i_nelements > min_base)
1081 min_base = clusters_b_i_nelements;
1087 if (clusters_b[index].nelements() <= 2 && nelements <= 5)
1088 return data_->baseline_clusters_.mean();
1090 return clusters_b[index].median();
1093 template <
typename L>
1095 line_info<L>::compute_meanline()
1100 float max_mean = 0.0f;
1101 const unsigned clusters_m_nelements = clusters_m.
nelements();
1103 if (clusters_m_nelements >= 3)
1104 return data_->meanline_clusters_.mean();
1106 for (
unsigned i = 0; i < clusters_m_nelements; ++i)
1108 const unsigned clusters_m_i_nelements = clusters_m[i].
nelements();
1110 if (clusters_m_i_nelements >= max_mean * 2.0f)
1112 max_mean = clusters_m_i_nelements;
1115 else if (clusters_m_i_nelements >= 0.5f * max_mean)
1117 if (clusters_m[index].
median() < clusters_m[i].
median())
1119 if (clusters_m_i_nelements > max_mean)
1120 max_mean = clusters_m_i_nelements;
1126 return clusters_m[index].median();
1129 template <
typename L>
1133 typedef mln_site(L) P;
1134 const component_set<L>& comp_set = data_->components_;
1137 typedef mln::
value::int_u<12> median_data_t;
1138 typedef mln::accu::stat::median_h<median_data_t> median_t;
1147 mln::accu::stat::mean<mln::
value::int_u<8> >
1159 unsigned pixel_area = 0;
1163 mln::def::
coord ref_line = mln_max(mln::def::
coord);
1166 data_->baseline_clusters_.reset();
1167 data_->meanline_clusters_.reset();
1173 for_all_elements(i, data_->component_ids_)
1175 unsigned c = data_->component_ids_(i);
1186 unsigned used_comps = 0;
1187 for_all_elements(i, data_->component_ids_)
1189 unsigned c = data_->component_ids_(i);
1191 pixel_area += comp_set(c).card();
1209 if (comp_set(c).has_features())
1212 boldness.take(comp_set(c).features().boldness);
1213 sum2_boldness += mln::math::sqr<float>(comp_set(c).features().boldness);
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());
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());
1236 if (data_->links_(c) != c)
1240 - comp_set(data_->links_(c)).
bbox().
pmax().col() - 1;
1244 char_space.take(space);
1251 if (bb.width() <= 1000)
1252 char_width.take(bb.width());
1258 data_->meanline_clusters_.take(bb.
pmin().
row());
1264 data_->baseline_clusters_.take(bb.
pmax().
row());
1270 data_->tag_ = line::None;
1273 data_->bbox_ = bbox.to_result();
1276 data_->pixel_area_ = pixel_area;
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));
1285 data_->boldness_ = boldness.to_result();
1286 data_->boldness_reliability_ =
std::sqrt(sum2_boldness);
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());
1304 if (char_space.card() < 2)
1305 data_->char_space_ = 0;
1307 data_->char_space_ = char_space.to_result();
1311 data_->char_width_ = (comp_set(data_->component_ids_[0]).bbox().width()
1312 + comp_set(data_->component_ids_[1]).bbox().width()) / 2;
1314 data_->char_width_ = char_width.to_result();
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;
1343 template <
typename L>
1345 operator<<(std::ostream& ostr, const line_info<L>& info)
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()
1375 <<
", text=" << info.text()
1376 <<
", html_text=" << info.html_text()
1377 <<
")" << std::endl;
1381 template <
typename L>
1383 operator==(
const line_info<L>& lhs,
const line_info<L>& rhs)
1385 if (! lhs.is_valid() && ! rhs.is_valid())
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();
1422 # endif// ! MLN_INCLUDE_ONLY
1428 #endif // ! SCRIBO_CORE_LINE_INFO_HH