15#include "config_auto.h"
19#include "allheaders.h"
48 : x_origin_(0), y_origin_(0), pix_(nullptr) {
51 if (scale_factor_ < 1) scale_factor_ = 1;
68 TBOX image_box(0, 0, pixGetWidth(nontext_map), pixGetHeight(nontext_map));
70 y_origin_ = image_box.
height();
71 int width = (image_box.
width() + scale_factor_ - 1) / scale_factor_;
72 int height = (image_box.
height() + scale_factor_ - 1) / scale_factor_;
74 pix_ = pixCreate(width, height, 8);
75 ProjectBlobs(&input_block->
blobs, rotation, image_box, nontext_map);
76 ProjectBlobs(&input_block->
large_blobs, rotation, image_box, nontext_map);
77 Pix* final_pix = pixBlockconv(pix_, 1, 1);
86#ifndef GRAPHICS_DISABLED
87 BLOBNBOX_IT it(blobs);
88 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
107 BLOBNBOX_LIST* blobs, BLOBNBOX_LIST* small_blobs)
const {
108 BLOBNBOX_IT it(blobs);
109 BLOBNBOX_IT small_it(small_blobs);
110 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
117 small_it.add_to_end(it.extract());
124#ifndef GRAPHICS_DISABLED
125 int width = pixGetWidth(pix_);
126 int height = pixGetHeight(pix_);
127 Pix* pixc = pixCreate(width, height, 32);
128 int src_wpl = pixGetWpl(pix_);
129 int col_wpl = pixGetWpl(pixc);
130 uint32_t* src_data = pixGetData(pix_);
131 uint32_t* col_data = pixGetData(pixc);
132 for (
int y = 0; y < height; ++y, src_data += src_wpl, col_data += col_wpl) {
133 for (
int x = 0; x < width; ++x) {
134 int pixel = GET_DATA_BYTE(src_data, x);
137 composeRGBPixel(0, 0, pixel * 15, &result);
138 else if (pixel <= 145)
139 composeRGBPixel(0, (pixel - 17) * 2, 255, &result);
141 composeRGBPixel((pixel - 145) * 2, 255, 255, &result);
142 col_data[x] = result;
145 auto* win =
new ScrollView(
"Projection", 0, 0,
146 width, height, width, height);
147 win->Image(pixc, 0, 0);
199 bool horizontal_textline,
204 int parallel_gap = 0;
209 if (horizontal_textline) {
210 parallel_gap = from_box.
x_gap(to_box) + from_box.
width();
211 start_pt.
x = (from_box.
left() + from_box.
right()) / 2;
212 end_pt.
x = start_pt.
x;
214 start_pt.
y = from_box.
top();
215 end_pt.
y = std::min(to_box.
top(), start_pt.
y);
217 start_pt.
y = from_box.
bottom();
218 end_pt.
y = std::max(to_box.
bottom(), start_pt.
y);
221 parallel_gap = from_box.
y_gap(to_box) + from_box.
height();
223 start_pt.
x = from_box.
right();
224 end_pt.
x = std::min(to_box.
right(), start_pt.
x);
226 start_pt.
x = from_box.
left();
227 end_pt.
x = std::max(to_box.
left(), start_pt.
x);
229 start_pt.
y = (from_box.
bottom() + from_box.
top()) / 2;
230 end_pt.
y = start_pt.
y;
236 int perpendicular_gap = 0;
240 if (start_pt.
x != end_pt.
x || start_pt.
y != end_pt.
y) {
241 if (denorm !=
nullptr) {
246 if (abs(start_pt.
y - end_pt.
y) >= abs(start_pt.
x - end_pt.
x)) {
277 int y1,
int y2)
const {
278 x = ImageXToProjectionX(x);
279 y1 = ImageYToProjectionY(y1);
280 y2 = ImageYToProjectionY(y2);
281 if (y1 == y2)
return 0;
282 int wpl = pixGetWpl(pix_);
283 int step = y1 < y2 ? 1 : -1;
284 uint32_t* data = pixGetData(pix_) + y1 * wpl;
286 int prev_pixel = GET_DATA_BYTE(data, x);
288 int right_way_steps = 0;
289 for (
int y = y1; y != y2; y += step) {
291 int pixel = GET_DATA_BYTE(data, x);
293 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
294 x, y + step, pixel, prev_pixel);
295 if (pixel < prev_pixel)
297 else if (pixel > prev_pixel)
303 return distance * scale_factor_ +
311 x1 = ImageXToProjectionX(x1);
312 x2 = ImageXToProjectionX(x2);
313 y = ImageYToProjectionY(y);
314 if (x1 == x2)
return 0;
315 int wpl = pixGetWpl(pix_);
316 int step = x1 < x2 ? 1 : -1;
317 uint32_t* data = pixGetData(pix_) + y * wpl;
318 int prev_pixel = GET_DATA_BYTE(data, x1);
320 int right_way_steps = 0;
321 for (
int x = x1; x != x2; x += step) {
322 int pixel = GET_DATA_BYTE(data, x + step);
324 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
325 x + step, y, pixel, prev_pixel);
326 if (pixel < prev_pixel)
328 else if (pixel > prev_pixel)
334 return distance * scale_factor_ +
346 EvaluateBoxInternal(box, denorm, debug, &grad1, &grad2,
nullptr,
nullptr);
347 int worst_result = std::min(grad1, grad2);
348 int total_result = grad1 + grad2;
349 if (total_result >= 6)
return false;
352 if (worst_result < 0)
381 tprintf(
"Partition hresult=%d, vresult=%d from:", hresult, vresult);
385 return hresult >= -vresult ? hresult : vresult;
415 return EvaluateBoxInternal(box, denorm, debug,
nullptr,
nullptr,
nullptr,
nullptr);
421int TextlineProjection::EvaluateBoxInternal(
const TBOX& box,
422 const DENORM* denorm,
bool debug,
423 int* hgrad1,
int* hgrad2,
424 int* vgrad1,
int* vgrad2)
const {
425 int top_gradient = BestMeanGradientInRow(denorm, box.
left(), box.
right(),
427 int bottom_gradient = -BestMeanGradientInRow(denorm, box.
left(), box.
right(),
429 int left_gradient = BestMeanGradientInColumn(denorm, box.
left(), box.
bottom(),
431 int right_gradient = -BestMeanGradientInColumn(denorm, box.
right(),
434 int top_clipped = std::max(top_gradient, 0);
435 int bottom_clipped = std::max(bottom_gradient, 0);
436 int left_clipped = std::max(left_gradient, 0);
437 int right_clipped = std::max(right_gradient, 0);
439 tprintf(
"Gradients: top = %d, bottom = %d, left= %d, right= %d for box:",
440 top_gradient, bottom_gradient, left_gradient, right_gradient);
443 int result = std::max(top_clipped, bottom_clipped) -
444 std::max(left_clipped, right_clipped);
445 if (hgrad1 !=
nullptr && hgrad2 !=
nullptr) {
446 *hgrad1 = top_gradient;
447 *hgrad2 = bottom_gradient;
449 if (vgrad1 !=
nullptr && vgrad2 !=
nullptr) {
450 *vgrad1 = left_gradient;
451 *vgrad2 = right_gradient;
461int TextlineProjection::BestMeanGradientInRow(
const DENORM* denorm,
462 int16_t min_x, int16_t max_x, int16_t y,
463 bool best_is_max)
const {
464 TPOINT start_pt(min_x, y);
466 int upper = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
467 int lower = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
468 int best_gradient = lower - upper;
469 upper = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
470 lower = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
471 int gradient = lower - upper;
472 if ((gradient > best_gradient) == best_is_max)
473 best_gradient = gradient;
474 upper = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
475 lower = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
476 gradient = lower - upper;
477 if ((gradient > best_gradient) == best_is_max)
478 best_gradient = gradient;
479 return best_gradient;
488int TextlineProjection::BestMeanGradientInColumn(
const DENORM* denorm, int16_t x,
489 int16_t min_y, int16_t max_y,
490 bool best_is_max)
const {
491 TPOINT start_pt(x, min_y);
493 int left = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
494 int right = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
495 int best_gradient = right - left;
496 left = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
497 right = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
498 int gradient = right - left;
499 if ((gradient > best_gradient) == best_is_max)
500 best_gradient = gradient;
501 left = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
502 right = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
503 gradient = right - left;
504 if ((gradient > best_gradient) == best_is_max)
505 best_gradient = gradient;
506 return best_gradient;
519int TextlineProjection::MeanPixelsInLineSegment(
const DENORM* denorm,
523 TransformToPixCoords(denorm, &start_pt);
524 TransformToPixCoords(denorm, &end_pt);
525 TruncateToImageBounds(&start_pt);
526 TruncateToImageBounds(&end_pt);
527 int wpl = pixGetWpl(pix_);
528 uint32_t* data = pixGetData(pix_);
531 int x_delta = end_pt.
x - start_pt.
x;
532 int y_delta = end_pt.
y - start_pt.
y;
533 if (abs(x_delta) >= abs(y_delta)) {
537 int x_step = x_delta > 0 ? 1 : -1;
540 start_pt.
y += offset;
542 TruncateToImageBounds(&start_pt);
543 TruncateToImageBounds(&end_pt);
544 x_delta = end_pt.
x - start_pt.
x;
545 y_delta = end_pt.
y - start_pt.
y;
546 count = x_delta * x_step + 1;
547 for (
int x = start_pt.
x; x != end_pt.
x; x += x_step) {
548 int y = start_pt.
y +
DivRounded(y_delta * (x - start_pt.
x), x_delta);
549 total += GET_DATA_BYTE(data + wpl * y, x);
553 int y_step = y_delta > 0 ? 1 : -1;
557 start_pt.
x += offset;
559 TruncateToImageBounds(&start_pt);
560 TruncateToImageBounds(&end_pt);
561 x_delta = end_pt.
x - start_pt.
x;
562 y_delta = end_pt.
y - start_pt.
y;
563 count = y_delta * y_step + 1;
564 for (
int y = start_pt.
y; y != end_pt.
y; y += y_step) {
565 int x = start_pt.
x +
DivRounded(x_delta * (y - start_pt.
y), y_delta);
566 total += GET_DATA_BYTE(data + wpl * y, x);
577static TBOX BoundsWithinBox(Pix* pix,
const TBOX& box) {
578 int im_height = pixGetHeight(pix);
579 Box* input_box = boxCreate(box.
left(), im_height - box.
top(),
581 Box* output_box =
nullptr;
582 pixClipBoxToForeground(pix, input_box,
nullptr, &output_box);
584 if (output_box !=
nullptr) {
585 l_int32 x, y, width, height;
586 boxGetGeometry(output_box, &x, &y, &width, &height);
589 result_box.
set_top(im_height - y);
591 boxDestroy(&output_box);
593 boxDestroy(&input_box);
601static void TruncateBoxToMissNonText(
int x_middle,
int y_middle,
602 bool split_on_x, Pix* nontext_map,
609 im_box = BoundsWithinBox(nontext_map, box1);
611 box2.set_left(x_middle);
612 im_box = BoundsWithinBox(nontext_map, box2);
615 box1.set_bottom(y_middle);
616 im_box = BoundsWithinBox(nontext_map, box1);
618 box2.set_top(y_middle);
619 im_box = BoundsWithinBox(nontext_map, box2);
620 if (!im_box.
null_box()) box2.set_bottom(im_box.
top());
629void TextlineProjection::IncrementRectangle8Bit(
const TBOX& box) {
630 int scaled_left = ImageXToProjectionX(box.
left());
631 int scaled_top = ImageYToProjectionY(box.
top());
632 int scaled_right = ImageXToProjectionX(box.
right());
633 int scaled_bottom = ImageYToProjectionY(box.
bottom());
634 int wpl = pixGetWpl(pix_);
635 uint32_t* data = pixGetData(pix_) + scaled_top * wpl;
636 for (
int y = scaled_top; y <= scaled_bottom; ++y) {
637 for (
int x = scaled_left; x <= scaled_right; ++x) {
638 int pixel = GET_DATA_BYTE(data, x);
640 SET_DATA_BYTE(data, x, pixel + 1);
652void TextlineProjection::ProjectBlobs(BLOBNBOX_LIST* blobs,
654 const TBOX& nontext_map_box,
656 BLOBNBOX_IT blob_it(blobs);
657 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
662 bool spreading_horizontally = PadBlobBox(blob, &bbox);
665 middle.rotate(rotation);
666 if (rotation.
x() == 0.0f)
667 spreading_horizontally = !spreading_horizontally;
669 bbox &= nontext_map_box;
671 TruncateBoxToMissNonText(middle.x(), middle.y(), spreading_horizontally,
673 if (bbox.
area() > 0) {
674 IncrementRectangle8Bit(bbox);
682bool TextlineProjection::PadBlobBox(
BLOBNBOX* blob,
TBOX* bbox) {
692 bool padding_horizontally =
false;
695 padding_horizontally =
true;
703 ypad = scale_factor_;
711 xpad = scale_factor_;
725 padding_horizontally =
true;
728 bbox->
pad(xpad, ypad);
738 return padding_horizontally;
743void TextlineProjection::TransformToPixCoords(
const DENORM* denorm,
745 if (denorm !=
nullptr) {
749 pt->
x = ImageXToProjectionX(pt->
x);
750 pt->
y = ImageYToProjectionY(pt->
y);
753#if defined(_MSC_VER) && !defined(__clang__)
754#pragma optimize("g", off)
757void TextlineProjection::TruncateToImageBounds(
TPOINT* pt)
const {
758 pt->
x = ClipToRange<int>(pt->
x, 0, pixGetWidth(pix_) - 1);
759 pt->
y = ClipToRange<int>(pt->
y, 0, pixGetHeight(pix_) - 1);
761#if defined(_MSC_VER) && !defined(__clang__)
762#pragma optimize("", on)
766int TextlineProjection::ImageXToProjectionX(
int x)
const {
767 x =
ClipToRange((x - x_origin_) / scale_factor_, 0, pixGetWidth(pix_) - 1);
770int TextlineProjection::ImageYToProjectionY(
int y)
const {
771 y =
ClipToRange((y_origin_ - y) / scale_factor_, 0, pixGetHeight(pix_) - 1);
int DivRounded(int a, int b)
int IntCastRounded(double x)
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
DLLSYM void tprintf(const char *format,...)
const int kWrongWayPenalty
const int kMaxTabStopOverrun
const int kParaPerpDistRatio
const int kMinLineSpacingFactor
const int kOrientedPadFactor
const int kDefaultPadFactor
BLOBNBOX * neighbour(BlobNeighbourDir n) const
const TBOX & bounding_box() const
bool UniquelyHorizontal() const
bool UniquelyVertical() const
BLOBNBOX_LIST large_blobs
void DenormTransform(const DENORM *last_denorm, const TPOINT &pt, TPOINT *original) const
void rotate(const FCOORD &vec)
int y_gap(const TBOX &box) const
void pad(int xpad, int ypad)
int x_gap(const TBOX &box) const
static bool WithinTestRegion(int detail_level, int x, int y)
int median_bottom() const
const TBOX & bounding_box() const
bool IsHorizontalType() const
bool BoxOutOfHTextline(const TBOX &box, const DENORM *denorm, bool debug) const
int DistanceOfBoxFromPartition(const TBOX &box, const ColPartition &part, const DENORM *denorm, bool debug) const
int EvaluateBox(const TBOX &box, const DENORM *denorm, bool debug) const
TextlineProjection(int resolution)
void MoveNonTextlineBlobs(BLOBNBOX_LIST *blobs, BLOBNBOX_LIST *small_blobs) const
int EvaluateColPartition(const ColPartition &part, const DENORM *denorm, bool debug) const
void ConstructProjection(TO_BLOCK *input_block, const FCOORD &rotation, Pix *nontext_map)
int DistanceOfBoxFromBox(const TBOX &from_box, const TBOX &to_box, bool horizontal_textline, const DENORM *denorm, bool debug) const
void PlotGradedBlobs(BLOBNBOX_LIST *blobs, ScrollView *win)
void DisplayProjection() const
int HorizontalDistance(bool debug, int x1, int x2, int y) const
int VerticalDistance(bool debug, int x, int y1, int y2) const
void Rectangle(int x1, int y1, int x2, int y2)