21# include "config_auto.h"
33#include <allheaders.h>
39#define PROJECTION_MARGIN 10
56 cblob_ptr->
rotate(rotation);
63 int left = -box.
right();
75 ICOORD bottom_pt(top_pt.
x(), base_char_bottom_);
77 base_char_top_ = top_pt.
y();
78 bottom_pt.
rotate(rotation);
79 base_char_bottom_ = bottom_pt.
y();
97 nextblob->joined =
true;
103 if (other->cblob_ptr !=
nullptr) {
104 C_OUTLINE_IT ol_it(cblob_ptr->
out_list());
105 ol_it.add_list_after(other->cblob_ptr->
out_list());
119 BLOBNBOX_IT *start_it,
132 float test_ymin, test_ymax;
137 blobcount =
static_cast<int16_t
>(std::floor(box.
width() / xheight));
138 if (blobcount > 1 && cblob_ptr !=
nullptr) {
140 blobwidth =
static_cast<float>(box.
width() + 1) / blobcount;
141 for (blobindex = blobcount - 1, rightx = box.
right(); blobindex >= 0;
142 blobindex--, rightx -= blobwidth) {
143 ymin =
static_cast<float>(INT32_MAX);
144 ymax =
static_cast<float>(-INT32_MAX);
147 blob = blob_it.data();
149 test_ymin, test_ymax);
152 }
while (blob != end_it->data());
154 leftx =
static_cast<int16_t
>(std::floor(rightx - blobwidth));
155 if (leftx < box.
left()) {
158 bl =
ICOORD(leftx,
static_cast<int16_t
>(std::floor(ymin)));
159 tr =
ICOORD(
static_cast<int16_t
>(std::ceil(rightx)),
static_cast<int16_t
>(std::ceil(ymax)));
160 if (blobindex == 0) {
165 newblob->box =
TBOX(bl, tr);
167 newblob->base_char_top_ = tr.
y();
168 newblob->base_char_bottom_ = bl.
y();
169 end_it->add_after_stay_put(newblob);
179 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
180 gaps[dir] = INT16_MAX;
185 gaps[dir] = box.
x_gap(n_box);
187 gaps[dir] = box.
y_gap(n_box);
198 int max_dimension = std::max(box.
width(), box.
height());
203 if (*h_max > max_dimension && *h_min < max_dimension) {
208 if (*v_max > max_dimension && *v_min < max_dimension) {
215 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
218 neighbours_[dir] =
nullptr;
219 good_stroke_neighbours_[dir] =
false;
228 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
240 for (
int dir = 0; dir <
BND_COUNT; ++dir) {
255 if (
cblob() ==
nullptr) {
258 int box_perimeter = 2 * (box.
height() + box.
width());
269 perimeter -= 4 *
cblob()->
area() / perimeter;
271 perimeter -= 2 * box.
width();
285 perimeter -= 4 *
cblob()->
area() / perimeter;
287 perimeter -= 2 * box.
height();
299 if (box.
left() < other.box.
left() && box.
left() < other.left_rule_) {
302 if (other.box.
left() < box.
left() && other.box.
left() < left_rule_) {
305 if (box.
right() > other.box.
right() && box.
right() > other.right_rule_) {
308 if (other.box.
right() > box.
right() && other.box.
right() > right_rule_) {
316 double constant_tolerance)
const {
321 float h_tolerance = horz_stroke_width_ * fractional_tolerance + constant_tolerance;
322 float v_tolerance = vert_stroke_width_ * fractional_tolerance + constant_tolerance;
323 double p_tolerance = p_width * fractional_tolerance + constant_tolerance;
324 bool h_zero = horz_stroke_width_ == 0.0f || other.horz_stroke_width_ == 0.0f;
325 bool v_zero = vert_stroke_width_ == 0.0f || other.vert_stroke_width_ == 0.0f;
326 bool h_ok = !h_zero &&
NearlyEqual(horz_stroke_width_, other.horz_stroke_width_, h_tolerance);
327 bool v_ok = !v_zero &&
NearlyEqual(vert_stroke_width_, other.vert_stroke_width_, v_tolerance);
328 bool p_ok = h_zero && v_zero &&
NearlyEqual(p_width, n_p_width, p_tolerance);
332 return p_ok || ((v_ok || h_ok) && (h_ok || h_zero) && (v_ok || v_zero));
338 FCOORD no_rotation(1.0f, 0.0f);
339 float top = box.
top();
340 float bottom = box.
bottom();
341 if (cblob_ptr !=
nullptr) {
342 find_cblob_limits(cblob_ptr,
static_cast<float>(left),
static_cast<float>(right), no_rotation,
350 FCOORD bot_left(left, bottom);
351 FCOORD top_right(right, top);
352 TBOX shrunken_box(bot_left);
353 TBOX shrunken_box2(top_right);
354 shrunken_box += shrunken_box2;
361 baseline_y_ = box.
bottom();
362 if (cblob_ptr ==
nullptr) {
370 BLOBNBOX_IT blob_it(blobs);
371 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
372 blob_it.data()->CleanNeighbours();
378 BLOBNBOX_IT blob_it(blobs);
379 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
383 delete blob_it.extract();
393 int scale_factor = 1;
394 if (thresholds !=
nullptr && grey !=
nullptr) {
395 grey_height = pixGetHeight(grey);
396 thr_height = pixGetHeight(thresholds);
397 scale_factor =
IntCastRounded(
static_cast<double>(grey_height) / thr_height);
399 BLOBNBOX_IT blob_it(blobs);
400 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
402 if (blob->
cblob() !=
nullptr) {
404 l_uint32 threshold = 128;
405 if (thresholds !=
nullptr && grey !=
nullptr) {
409 pixGetPixel(thresholds, pt.
x / scale_factor, thr_height - 1 - pt.
y / scale_factor,
417#ifndef GRAPHICS_DISABLED
422 BLOBNBOX_IT it(list);
423 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
424 it.data()->plot(win, body_colour, child_colour);
433 BLOBNBOX_IT it(list);
434 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
437 blob->
plot(win, body_colour, child_colour);
492 if (cblob_ptr !=
nullptr) {
493 cblob_ptr->
plot(window, blob_colour, child_colour);
516 C_OUTLINE_IT out_it = blob->
out_list();
518 ymin =
static_cast<float>(INT32_MAX);
519 ymax =
static_cast<float>(-INT32_MAX);
520 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
521 outline = out_it.data();
524 for (stepindex = 0; stepindex < outline->
pathlength(); stepindex++) {
526 if (pos.
x() >= leftx && pos.
x() <= rightx) {
529 vec = outline->
step(stepindex);
554 C_OUTLINE_IT out_it = blob->
out_list();
556 ymin =
static_cast<float>(INT32_MAX);
557 ymax =
static_cast<float>(-INT32_MAX);
558 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
559 outline = out_it.data();
561 for (stepindex = 0; stepindex < outline->
pathlength(); stepindex++) {
563 if (pos.
x() >= leftx && pos.
x() <= rightx) {
566 vec = outline->
step(stepindex);
590 C_OUTLINE_IT out_it = blob->
out_list();
592 xmin =
static_cast<float>(INT32_MAX);
593 xmax =
static_cast<float>(-INT32_MAX);
594 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
595 outline = out_it.data();
597 for (stepindex = 0; stepindex < outline->
pathlength(); stepindex++) {
599 if (pos.
y() >= bottomy && pos.
y() <= topy) {
602 vec = outline->
step(stepindex);
618 C_OUTLINE_LIST out_list;
620 C_OUTLINE_IT in_it = blob->
out_list();
622 C_OUTLINE_IT out_it = &out_list;
624 for (in_it.mark_cycle_pt(); !in_it.cycled_list(); in_it.forward()) {
625 out_it.add_after_then_move(
new C_OUTLINE(in_it.data(), rotation));
627 return new C_BLOB(&out_list);
649 if (blob->
cblob() ==
nullptr) {
699 initial_y_min = bottom;
702 BLOBNBOX_IT it = &blobs;
705 diff = top - bottom - row_size;
711 else if ((top - bottom) * 3 < row_size) {
712 diff = row_size / 3 + bottom - top;
720 "pitch=%d, fp=%g, fps=%g, fpns=%g, prs=%g, prns=%g,"
721 " spacing=%g xh=%g y_origin=%g xev=%d, asc=%g, desc=%g,"
722 " body=%g, minsp=%d maxnsp=%d, thr=%d kern=%g sp=%g\n",
742 BLOBNBOX_IT it = &blobs;
745 allowed = row_size + y_min - y_max;
747 available = top > y_max ? top - y_max : 0;
748 if (bottom < y_min) {
750 available += y_min - bottom;
753 available += available;
754 if (available < allowed) {
757 if (bottom < y_min) {
758 y_min -= (y_min - bottom) * allowed / available;
761 y_max += (top - y_max) * allowed / available;
776 BLOBNBOX_IT it = &blobs;
779 it.add_before_then_move(blob);
782 while (!it.cycled_list() && it.data()->bounding_box().left() <= blob->
bounding_box().
left()) {
785 if (it.cycled_list()) {
788 it.add_before_stay_put(blob);
805 if (blob_it.empty()) {
808 row_box = blob_it.data()->bounding_box();
809 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
810 row_box += blob_it.data()->bounding_box();
816 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
817 blob = blob_it.data();
818 if (blob->
cblob() !=
nullptr) {
829void TO_ROW::clear() {
861 num_repeated_sets_ = -1;
876 C_OUTLINE_IT out_it = blob->
out_list();
878 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
898 C_OUTLINE_IT out_it = outline->
child();
902 for (stepindex = 0; stepindex < length; stepindex++) {
903 step = outline->
step(stepindex);
905 stats->
add(pos.
x(), -pos.
y());
906 }
else if (step.
x() < 0) {
907 stats->
add(pos.
x() - 1, pos.
y());
912 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
969static void SizeFilterBlobs(
int min_height,
int max_height, BLOBNBOX_LIST *src_list,
970 BLOBNBOX_LIST *noise_list, BLOBNBOX_LIST *small_list,
971 BLOBNBOX_LIST *medium_list, BLOBNBOX_LIST *large_list) {
972 BLOBNBOX_IT noise_it(noise_list);
973 BLOBNBOX_IT small_it(small_list);
974 BLOBNBOX_IT medium_it(medium_list);
975 BLOBNBOX_IT large_it(large_list);
976 for (BLOBNBOX_IT src_it(src_list); !src_it.empty(); src_it.forward()) {
981 if (height < min_height && (width < min_height || width > max_height)) {
982 noise_it.add_after_then_move(blob);
983 }
else if (height > max_height) {
984 large_it.add_after_then_move(blob);
985 }
else if (height < min_height) {
986 small_it.add_after_then_move(blob);
988 medium_it.add_after_then_move(blob);
1001 BLOBNBOX_LIST noise_list;
1002 BLOBNBOX_LIST small_list;
1003 BLOBNBOX_LIST medium_list;
1004 BLOBNBOX_LIST large_list;
1005 SizeFilterBlobs(min_height, max_height, &
blobs, &noise_list, &small_list, &medium_list,
1007 SizeFilterBlobs(min_height, max_height, &
large_blobs, &noise_list, &small_list, &medium_list,
1009 SizeFilterBlobs(min_height, max_height, &
small_blobs, &noise_list, &small_list, &medium_list,
1011 SizeFilterBlobs(min_height, max_height, &
noise_blobs, &noise_list, &small_list, &medium_list,
1013 BLOBNBOX_IT blob_it(&
blobs);
1014 blob_it.add_list_after(&medium_list);
1016 blob_it.add_list_after(&large_list);
1018 blob_it.add_list_after(&small_list);
1020 blob_it.add_list_after(&noise_list);
1048#ifndef GRAPHICS_DISABLED
1072 BLOBNBOX_LIST *list,
1075 BLOBNBOX_IT it = list;
1076 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1077 it.data()->plot(win, body_colour, child_colour);
#define PROJECTION_MARGIN
const double kComplexShapePerimeterRatio
C_BLOB * crotate_cblob(C_BLOB *blob, FCOORD rotation)
bool NearlyEqual(T x, T y, T tolerance)
void tprintf(const char *format,...)
int IntCastRounded(double x)
const double kMaxMediumSizeRatio
void vertical_cblob_projection(C_BLOB *blob, STATS *stats)
void find_cblob_limits(C_BLOB *blob, float leftx, float rightx, FCOORD rotation, float &ymin, float &ymax)
void plot_blob_list(ScrollView *win, BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour)
const double kMinMediumSizeRatio
void find_cblob_hlimits(C_BLOB *blob, float bottomy, float topy, float &xmin, float &xmax)
void vertical_coutline_projection(C_OUTLINE *outline, STATS *stats)
const double kDefiniteAspectRatio
void UpdateRange(const T1 &x, T2 *lower_bound, T2 *upper_bound)
void find_cblob_vlimits(C_BLOB *blob, float leftx, float rightx, float &ymin, float &ymax)
TBOX box_next_pre_chopped(BLOBNBOX_IT *it)
const double kCosSmallAngle
TBOX box_next(BLOBNBOX_IT *it)
float vert_stroke_width() const
void set_vert_possible(bool value)
bool good_stroke_neighbour(BlobNeighbourDir n) const
const TBOX & bounding_box() const
bool DeletableNoise() const
void NeighbourGaps(int gaps[BND_COUNT]) const
bool MatchingStrokeWidth(const BLOBNBOX &other, double fractional_tolerance, double constant_tolerance) const
BlobRegionType region_type() const
void set_horz_possible(bool value)
int NoisyNeighbours() const
void rotate(FCOORD rotation)
void really_merge(BLOBNBOX *other)
void reflect_box_in_y_axis()
void rotate_box(FCOORD rotation)
static void PlotNoiseBlobs(BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView *win)
void EstimateBaselinePosition()
void merge(BLOBNBOX *nextblob)
ScrollView::Color BoxColor() const
void MinMaxGapsClipped(int *h_min, int *h_max, int *v_min, int *v_max) const
void plot(ScrollView *window, ScrollView::Color blob_colour, ScrollView::Color child_colour)
static void clear_blobnboxes(BLOBNBOX_LIST *boxes)
void set_diacritic_box(const TBOX &diacritic_box)
static void PlotBlobs(BLOBNBOX_LIST *list, ScrollView::Color body_colour, ScrollView::Color child_colour, ScrollView *win)
TBOX BoundsWithinLimits(int left, int right)
void chop(BLOBNBOX_IT *start_it, BLOBNBOX_IT *blob_it, FCOORD rotation, float xheight)
BLOBNBOX * neighbour(BlobNeighbourDir n) const
bool DefiniteIndividualFlow()
bool ConfirmNoTabViolation(const BLOBNBOX &other) const
float area_stroke_width() const
void compute_bounding_box()
static void ComputeEdgeOffsets(Image thresholds, Image grey, BLOBNBOX_LIST *blobs)
float horz_stroke_width() const
static void DeleteNoiseBlobs(BLOBNBOX_LIST *blobs)
static ScrollView::Color TextlineColor(BlobRegionType region_type, BlobTextFlowType flow_type)
bool joined_to_prev() const
void add_blob(BLOBNBOX *blob, float top, float bottom, float row_size)
BLOBNBOX_LIST * blob_list()
void compute_vertical_projection()
void insert_blob(BLOBNBOX *blob)
PITCH_TYPE pitch_decision
void ReSetAndReFilterBlobs()
BLOBNBOX_LIST small_blobs
void plot_graded_blobs(ScrollView *to_win)
void plot_noise_blobs(ScrollView *to_win)
void DeleteUnownedNoise()
BLOBNBOX_LIST large_blobs
BLOBNBOX_LIST noise_blobs
void ComputeEdgeOffsets(Image thresholds, Image grey)
PITCH_TYPE pitch_decision
int32_t pathlength() const
ICOORD step(int index) const
const ICOORD & start_pos() const
void rotate(const FCOORD &vec)
TDimension y() const
access_function
TDimension x() const
access function
int y_gap(const TBOX &box) const
TDimension height() const
void rotate(const FCOORD &vec)
int x_gap(const TBOX &box) const
TDimension bottom() const
void add(int32_t value, int32_t count)
bool set_range(int32_t min_bucket_value, int32_t max_bucket_value)
void rotate(const FCOORD &rotation)
void plot(ScrollView *window, ScrollView::Color blob_colour, ScrollView::Color child_colour)
C_OUTLINE_LIST * out_list()
TBOX bounding_box() const
void ComputeEdgeOffsets(int threshold, Image pix)
int16_t EstimateBaselinePosition()