Skip to content

Commit

Permalink
Fixed pixCropImage() when aggressively cropping 2-column material.
Browse files Browse the repository at this point in the history
* So it selects both columns, not just the one with largest area.
  • Loading branch information
DanBloomberg committed Jul 13, 2024
1 parent 913550c commit bf127eb
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 21 deletions.
7 changes: 6 additions & 1 deletion prog/croppdf.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
*
* Syntax:
* croppdf basedir lrclear tbclear edgeclean lradd tbadd maxwiden
* title fileout
* printwiden title fileout
*
* The %basedir is a directory where the input pdf files are located.
* The program will operate on every file in this directory with
Expand All @@ -62,6 +62,11 @@
* 8.5 x 11 inch printed page. It gives the maximum fractional horizontal
* stretching allowed. Suggested values are between 1.0 and 1.15.
*
* If you are not concerned with printing on paper, use the default
* value 0 for %printwiden to skip; 1 for 8.5 x 11 paper; 2 for A4.
* Widening only takes place if the ratio h/w exceeds the specified paper
* size by 3%, and the horizontal scaling factor will not exceed 1.20.
*
* The %title is the title given to the pdf. Use %title == "none"
* to omit the title.
*
Expand Down
54 changes: 34 additions & 20 deletions src/pageseg.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
* Halftone region extraction
* PIX *pixGenHalftoneMask() **Deprecated wrapper**
* PIX *pixGenerateHalftoneMask()
*
* Textline extraction
* PIX *pixGenTextlineMask()
Expand Down Expand Up @@ -559,8 +558,10 @@ PIX *pix1, *pix2, *pix3, *pixd;
* (c) If %edgeclean > 0, it removes isolated sets of pixels,
* using a close/open operation of size %edgeclean + 1.
* If %edgeclean < 0, it uses a large vertical morphological
* closing and the extraction of the largest resulting
* connected component to eliminate noise on left and right sides.
* close/open and the extraction of either the largest
* resulting connected component (or the largest two components
* if the page has 2 columns), to eliminate noise on left
* and right sides.
* (d) Find the bounding box of remaining fg pixels and scales
* the box up 2x back to full resolution.
* (e) Crops the binarized image to the bounding box.
Expand Down Expand Up @@ -705,14 +706,15 @@ PIXA *pixa1;
/* Optionally widen image if possible, for printing on 8.5 x 11 inch
* or A4 paper. Specifically, widen the image if the h/w asperity
* ratio of the input image exceeds that of the selected paper by
* more than 3%. Do not widen by more than 25%. */
* more than 3%. Do not widen by more than 20%. */
r1 = (l_float32)h / (l_float32)w;
r2 = 0.0; /* for default case */
if (printwiden == 1) /* standard */
r2 = r1 / 1.294;
else if (printwiden == 2) /* A4 */
r2 = r1 / 1.414;
if (r2 > 1.03) {
r2 = L_MIN(r2, 1.20);
lept_stderr("oversize h/w ratio by factor %6.3f\n", r2);
pix4 = pixScale(pix3, r2, 1.0);
} else {
Expand Down Expand Up @@ -748,18 +750,21 @@ PIXA *pixa1;
* at the input resolution.
* (2) The input %pixs should be at a resolution 100 - 150 ppi.
* (3) It does two 2x level1 rank binary reductions, followed
* by a large vertical close/open, and then a 4x expansion
* back to the input resolution.
* (4) It is used as an option to pixCropImage(), when given
* by a large vertical close/open, with a very small horizontal
* close/oopen, and then a 4x expansion back to the input resolution.
* (4) To work properly with 2-column layout, if the largest and
* second-largest regions are comparable in size, both are included.
* (5) This is used as an option to pixCropImage(), when given
* a negative %edgecrop parameter.
* </pre>
*/
l_int32
pixMaxCompAfterVClosing(PIX *pixs,
BOX **pbox)
{
l_int32 w, h, i, n, maxindex, maxarea, empty;
BOXA *boxa1;
l_int32 w1, h1, w2, h2, n, empty;
BOX *box1, *box2;
BOXA *boxa1, *boxa2;
PIX *pix1;

if (!pbox)
Expand All @@ -769,26 +774,35 @@ PIX *pix1;
return ERROR_INT("pixs undefined or not 1 bpp", __func__, 1);

/* Strong vertical closing */
pix1 = pixMorphSequence(pixs, "r11 + c1.50 + o1.50 + x4", 0);
pix1 = pixMorphSequence(pixs, "r11 + c3.80 + o3.80 + x4", 0);
pixZero(pix1, &empty);
if (empty)
return ERROR_INT("pix1 is empty", __func__, 1);

/* Find the c.c. with largest area and return its bounding box */
/* Find the two c.c. with largest area. If they are not comparable
* in area, return the bounding box of the largest; otherwise,
* return the bounding box of both regions. */
boxa1 = pixConnCompBB(pix1, 8);
pixDestroy(&pix1);
n = boxaGetCount(boxa1);
maxindex = 0;
maxarea = 0;
for (i = 0; i < n; i++) {
boxaGetBoxGeometry(boxa1, i, NULL, NULL, &w, &h);
if (w * h > maxarea) {
maxarea = w * h;
maxindex = i;
boxa2 = boxaSort(boxa1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL);
box1 = boxaGetBox(boxa2, 0, L_COPY);
if ((n = boxaGetCount(boxa2)) == 1) {
*pbox = box1;
} else { /* 2 or more */
box1 = boxaGetBox(boxa2, 0, L_COPY);
box2 = boxaGetBox(boxa2, 1, L_COPY);
boxGetGeometry(box1, NULL, NULL, &w1, &h1);
boxGetGeometry(box2, NULL, NULL, &w2, &h2);
if (((l_float32)(w2 * h2) / (l_float32)(w1 * h1)) > 0.7) {
*pbox = boxBoundingRegion(box1, box2);
boxDestroy(&box1);
} else {
*pbox = box1;
}
boxDestroy(&box2);
}
*pbox = boxaGetBox(boxa1, maxindex, L_COPY);
boxaDestroy(&boxa1);
boxaDestroy(&boxa2);
return 0;
}

Expand Down

0 comments on commit bf127eb

Please sign in to comment.