19 #include "allheaders.h" 46 : x_origin_(0), y_origin_(0), pix_(NULL) {
49 if (scale_factor_ < 1) scale_factor_ = 1;
66 TBOX image_box(0, 0, pixGetWidth(nontext_map), pixGetHeight(nontext_map));
68 y_origin_ = image_box.
height();
69 int width = (image_box.
width() + scale_factor_ - 1) / scale_factor_;
70 int height = (image_box.
height() + scale_factor_ - 1) / scale_factor_;
72 pix_ = pixCreate(width, height, 8);
73 ProjectBlobs(&input_block->
blobs, rotation, image_box, nontext_map);
74 ProjectBlobs(&input_block->
large_blobs, rotation, image_box, nontext_map);
75 Pix* final_pix = pixBlockconv(pix_, 1, 1);
84 #ifndef GRAPHICS_DISABLED 85 BLOBNBOX_IT it(blobs);
86 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
97 #endif // GRAPHICS_DISABLED 105 BLOBNBOX_LIST* blobs, BLOBNBOX_LIST* small_blobs)
const {
106 BLOBNBOX_IT it(blobs);
107 BLOBNBOX_IT small_it(small_blobs);
108 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
115 small_it.add_to_end(it.extract());
122 int width = pixGetWidth(pix_);
123 int height = pixGetHeight(pix_);
124 Pix* pixc = pixCreate(width, height, 32);
125 int src_wpl = pixGetWpl(pix_);
126 int col_wpl = pixGetWpl(pixc);
127 uinT32* src_data = pixGetData(pix_);
128 uinT32* col_data = pixGetData(pixc);
129 for (
int y = 0; y < height; ++y, src_data += src_wpl, col_data += col_wpl) {
130 for (
int x = 0; x < width; ++x) {
131 int pixel = GET_DATA_BYTE(src_data, x);
134 composeRGBPixel(0, 0, pixel * 15, &result);
135 else if (pixel <= 145)
136 composeRGBPixel(0, (pixel - 17) * 2, 255, &result);
138 composeRGBPixel((pixel - 145) * 2, 255, 255, &result);
139 col_data[x] = result;
145 width, height, width, height);
146 win->
Image(pixc, 0, 0);
149 pixWrite(
"projection.png", pixc, IFF_PNG);
200 bool horizontal_textline,
205 int parallel_gap = 0;
210 if (horizontal_textline) {
211 parallel_gap = from_box.
x_gap(to_box) + from_box.
width();
212 start_pt.
x = (from_box.
left() + from_box.
right()) / 2;
213 end_pt.
x = start_pt.
x;
215 start_pt.
y = from_box.
top();
216 end_pt.
y =
MIN(to_box.
top(), start_pt.
y);
218 start_pt.
y = from_box.
bottom();
222 parallel_gap = from_box.
y_gap(to_box) + from_box.
height();
224 start_pt.
x = from_box.
right();
227 start_pt.
x = from_box.
left();
228 end_pt.
x =
MAX(to_box.
left(), start_pt.
x);
230 start_pt.
y = (from_box.
bottom() + from_box.
top()) / 2;
231 end_pt.
y = start_pt.
y;
237 int perpendicular_gap = 0;
241 if (start_pt.
x != end_pt.
x || start_pt.
y != end_pt.
y) {
242 if (denorm != NULL) {
247 if (abs(start_pt.
y - end_pt.
y) >= abs(start_pt.
x - end_pt.
x)) {
278 int y1,
int y2)
const {
279 x = ImageXToProjectionX(x);
280 y1 = ImageYToProjectionY(y1);
281 y2 = ImageYToProjectionY(y2);
282 if (y1 == y2)
return 0;
283 int wpl = pixGetWpl(pix_);
284 int step = y1 < y2 ? 1 : -1;
285 uinT32* data = pixGetData(pix_) + y1 * wpl;
287 int prev_pixel = GET_DATA_BYTE(data, x);
289 int right_way_steps = 0;
290 for (
int y = y1; y != y2; y += step) {
292 int pixel = GET_DATA_BYTE(data, x);
294 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
295 x, y + step, pixel, prev_pixel);
296 if (pixel < prev_pixel)
298 else if (pixel > prev_pixel)
304 return distance * scale_factor_ +
312 x1 = ImageXToProjectionX(x1);
313 x2 = ImageXToProjectionX(x2);
314 y = ImageYToProjectionY(y);
315 if (x1 == x2)
return 0;
316 int wpl = pixGetWpl(pix_);
317 int step = x1 < x2 ? 1 : -1;
318 uinT32* data = pixGetData(pix_) + y * wpl;
319 int prev_pixel = GET_DATA_BYTE(data, x1);
321 int right_way_steps = 0;
322 for (
int x = x1; x != x2; x += step) {
323 int pixel = GET_DATA_BYTE(data, x + step);
325 tprintf(
"At (%d,%d), pix = %d, prev=%d\n",
326 x + step, y, pixel, prev_pixel);
327 if (pixel < prev_pixel)
329 else if (pixel > prev_pixel)
335 return distance * scale_factor_ +
347 EvaluateBoxInternal(box, denorm, debug, &grad1, &grad2, NULL, NULL);
348 int worst_result =
MIN(grad1, grad2);
349 int total_result = grad1 + grad2;
350 if (total_result >= 6)
return false;
353 if (worst_result < 0)
382 tprintf(
"Partition hresult=%d, vresult=%d from:", hresult, vresult);
386 return hresult >= -vresult ? hresult : vresult;
416 return EvaluateBoxInternal(box, denorm, debug, NULL, NULL, NULL, NULL);
422 int TextlineProjection::EvaluateBoxInternal(
const TBOX& box,
423 const DENORM* denorm,
bool debug,
424 int* hgrad1,
int* hgrad2,
425 int* vgrad1,
int* vgrad2)
const {
426 int top_gradient = BestMeanGradientInRow(denorm, box.
left(), box.
right(),
428 int bottom_gradient = -BestMeanGradientInRow(denorm, box.
left(), box.
right(),
430 int left_gradient = BestMeanGradientInColumn(denorm, box.
left(), box.
bottom(),
432 int right_gradient = -BestMeanGradientInColumn(denorm, box.
right(),
435 int top_clipped =
MAX(top_gradient, 0);
436 int bottom_clipped =
MAX(bottom_gradient, 0);
437 int left_clipped =
MAX(left_gradient, 0);
438 int right_clipped =
MAX(right_gradient, 0);
440 tprintf(
"Gradients: top = %d, bottom = %d, left= %d, right= %d for box:",
441 top_gradient, bottom_gradient, left_gradient, right_gradient);
444 int result =
MAX(top_clipped, bottom_clipped) -
445 MAX(left_clipped, right_clipped);
446 if (hgrad1 != NULL && hgrad2 != NULL) {
447 *hgrad1 = top_gradient;
448 *hgrad2 = bottom_gradient;
450 if (vgrad1 != NULL && vgrad2 != NULL) {
451 *vgrad1 = left_gradient;
452 *vgrad2 = right_gradient;
462 int TextlineProjection::BestMeanGradientInRow(
const DENORM* denorm,
464 bool best_is_max)
const {
465 TPOINT start_pt(min_x, y);
467 int upper = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
468 int lower = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
469 int best_gradient = lower - upper;
470 upper = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
471 lower = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
472 int gradient = lower - upper;
473 if ((gradient > best_gradient) == best_is_max)
474 best_gradient = gradient;
475 upper = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
476 lower = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
477 gradient = lower - upper;
478 if ((gradient > best_gradient) == best_is_max)
479 best_gradient = gradient;
480 return best_gradient;
489 int TextlineProjection::BestMeanGradientInColumn(
const DENORM* denorm,
inT16 x,
491 bool best_is_max)
const {
492 TPOINT start_pt(x, min_y);
494 int left = MeanPixelsInLineSegment(denorm, -2, start_pt, end_pt);
495 int right = MeanPixelsInLineSegment(denorm, 2, start_pt, end_pt);
496 int best_gradient = right - left;
497 left = MeanPixelsInLineSegment(denorm, -1, start_pt, end_pt);
498 right = MeanPixelsInLineSegment(denorm, 3, start_pt, end_pt);
499 int gradient = right - left;
500 if ((gradient > best_gradient) == best_is_max)
501 best_gradient = gradient;
502 left = MeanPixelsInLineSegment(denorm, -3, start_pt, end_pt);
503 right = MeanPixelsInLineSegment(denorm, 1, start_pt, end_pt);
504 gradient = right - left;
505 if ((gradient > best_gradient) == best_is_max)
506 best_gradient = gradient;
507 return best_gradient;
520 int TextlineProjection::MeanPixelsInLineSegment(
const DENORM* denorm,
524 TransformToPixCoords(denorm, &start_pt);
525 TransformToPixCoords(denorm, &end_pt);
526 TruncateToImageBounds(&start_pt);
527 TruncateToImageBounds(&end_pt);
528 int wpl = pixGetWpl(pix_);
529 uinT32* data = pixGetData(pix_);
532 int x_delta = end_pt.
x - start_pt.
x;
533 int y_delta = end_pt.
y - start_pt.
y;
534 if (abs(x_delta) >= abs(y_delta)) {
538 int x_step = x_delta > 0 ? 1 : -1;
541 start_pt.
y += offset;
543 TruncateToImageBounds(&start_pt);
544 TruncateToImageBounds(&end_pt);
545 x_delta = end_pt.
x - start_pt.
x;
546 y_delta = end_pt.
y - start_pt.
y;
547 count = x_delta * x_step + 1;
548 for (
int x = start_pt.
x; x != end_pt.
x; x += x_step) {
549 int y = start_pt.
y +
DivRounded(y_delta * (x - start_pt.
x), x_delta);
550 total += GET_DATA_BYTE(data + wpl * y, x);
554 int y_step = y_delta > 0 ? 1 : -1;
558 start_pt.
x += offset;
560 TruncateToImageBounds(&start_pt);
561 TruncateToImageBounds(&end_pt);
562 x_delta = end_pt.
x - start_pt.
x;
563 y_delta = end_pt.
y - start_pt.
y;
564 count = y_delta * y_step + 1;
565 for (
int y = start_pt.
y; y != end_pt.
y; y += y_step) {
566 int x = start_pt.
x +
DivRounded(x_delta * (y - start_pt.
y), y_delta);
567 total += GET_DATA_BYTE(data + wpl * y, x);
578 static TBOX BoundsWithinBox(Pix* pix,
const TBOX& box) {
579 int im_height = pixGetHeight(pix);
580 Box* input_box = boxCreate(box.
left(), im_height - box.
top(),
582 Box* output_box = NULL;
583 pixClipBoxToForeground(pix, input_box, NULL, &output_box);
585 if (output_box != NULL) {
586 l_int32 x, y, width, height;
587 boxGetGeometry(output_box, &x, &y, &width, &height);
590 result_box.
set_top(im_height - y);
592 boxDestroy(&output_box);
594 boxDestroy(&input_box);
602 static void TruncateBoxToMissNonText(
int x_middle,
int y_middle,
603 bool split_on_x, Pix* nontext_map,
610 im_box = BoundsWithinBox(nontext_map, box1);
612 box2.set_left(x_middle);
613 im_box = BoundsWithinBox(nontext_map, box2);
616 box1.set_bottom(y_middle);
617 im_box = BoundsWithinBox(nontext_map, box1);
619 box2.set_top(y_middle);
620 im_box = BoundsWithinBox(nontext_map, box2);
621 if (!im_box.
null_box()) box2.set_bottom(im_box.
top());
630 void TextlineProjection::IncrementRectangle8Bit(
const TBOX& box) {
631 int scaled_left = ImageXToProjectionX(box.
left());
632 int scaled_top = ImageYToProjectionY(box.
top());
633 int scaled_right = ImageXToProjectionX(box.
right());
634 int scaled_bottom = ImageYToProjectionY(box.
bottom());
635 int wpl = pixGetWpl(pix_);
636 uinT32* data = pixGetData(pix_) + scaled_top * wpl;
637 for (
int y = scaled_top; y <= scaled_bottom; ++y) {
638 for (
int x = scaled_left; x <= scaled_right; ++x) {
639 int pixel = GET_DATA_BYTE(data, x);
641 SET_DATA_BYTE(data, x, pixel + 1);
653 void TextlineProjection::ProjectBlobs(BLOBNBOX_LIST* blobs,
655 const TBOX& nontext_map_box,
657 BLOBNBOX_IT blob_it(blobs);
658 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
663 bool spreading_horizontally = PadBlobBox(blob, &bbox);
666 middle.rotate(rotation);
667 if (rotation.
x() == 0.0f)
668 spreading_horizontally = !spreading_horizontally;
670 bbox &= nontext_map_box;
672 TruncateBoxToMissNonText(middle.x(), middle.y(), spreading_horizontally,
674 if (bbox.
area() > 0) {
675 IncrementRectangle8Bit(bbox);
683 bool TextlineProjection::PadBlobBox(
BLOBNBOX* blob,
TBOX* bbox) {
693 bool padding_horizontally =
false;
696 padding_horizontally =
true;
704 ypad = scale_factor_;
712 xpad = scale_factor_;
726 padding_horizontally =
true;
729 bbox->
pad(xpad, ypad);
739 return padding_horizontally;
744 void TextlineProjection::TransformToPixCoords(
const DENORM* denorm,
746 if (denorm != NULL) {
750 pt->
x = ImageXToProjectionX(pt->
x);
751 pt->
y = ImageYToProjectionY(pt->
y);
755 #pragma optimize("g", off) 758 void TextlineProjection::TruncateToImageBounds(
TPOINT* pt)
const {
759 pt->
x = ClipToRange<int>(pt->
x, 0, pixGetWidth(pix_) - 1);
760 pt->
y = ClipToRange<int>(pt->
y, 0, pixGetHeight(pix_) - 1);
763 #pragma optimize("", on) 767 int TextlineProjection::ImageXToProjectionX(
int x)
const {
768 x =
ClipToRange((x - x_origin_) / scale_factor_, 0, pixGetWidth(pix_) - 1);
771 int TextlineProjection::ImageYToProjectionY(
int y)
const {
772 y =
ClipToRange((y_origin_ - y) / scale_factor_, 0, pixGetHeight(pix_) - 1);
const TBOX & bounding_box() const
void rotate(const FCOORD &vec)
void Image(struct Pix *image, int x_pos, int y_pos)
void ConstructProjection(TO_BLOCK *input_block, const FCOORD &rotation, Pix *nontext_map)
int IntCastRounded(double x)
bool UniquelyVertical() const
const int kDefaultPadFactor
int HorizontalDistance(bool debug, int x1, int x2, int y) const
static bool WithinTestRegion(int detail_level, int x, int y)
T ClipToRange(const T &x, const T &lower_bound, const T &upper_bound)
void DenormTransform(const DENORM *last_denorm, const TPOINT &pt, TPOINT *original) const
void PlotGradedBlobs(BLOBNBOX_LIST *blobs, ScrollView *win)
int y_gap(const TBOX &box) const
BLOBNBOX * neighbour(BlobNeighbourDir n) const
const int kWrongWayPenalty
const int kOrientedPadFactor
const int kMinLineSpacingFactor
BLOBNBOX_LIST large_blobs
bool IsHorizontalType() const
int median_bottom() const
int x_gap(const TBOX &box) const
void Rectangle(int x1, int y1, int x2, int y2)
bool UniquelyHorizontal() const
void MoveNonTextlineBlobs(BLOBNBOX_LIST *blobs, BLOBNBOX_LIST *small_blobs) const
int DivRounded(int a, int b)
void pad(int xpad, int ypad)
int VerticalDistance(bool debug, int x, int y1, int y2) const
void DisplayProjection() const
int EvaluateBox(const TBOX &box, const DENORM *denorm, bool debug) const
bool BoxOutOfHTextline(const TBOX &box, const DENORM *denorm, bool debug) const
int EvaluateColPartition(const ColPartition &part, const DENORM *denorm, bool debug) const
int DistanceOfBoxFromPartition(const TBOX &box, const ColPartition &part, const DENORM *denorm, bool debug) const
TextlineProjection(int resolution)
const TBOX & bounding_box() const
const int kParaPerpDistRatio
int DistanceOfBoxFromBox(const TBOX &from_box, const TBOX &to_box, bool horizontal_textline, const DENORM *denorm, bool debug) const
const int kMaxTabStopOverrun