Leptonica  1.83.1
Image processing and image analysis suite
dewarp2.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
62 #ifdef HAVE_CONFIG_H
63 #include <config_auto.h>
64 #endif /* HAVE_CONFIG_H */
65 
66 #include <math.h>
67 #include "allheaders.h"
68 
69 static PTA *dewarpGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y);
70 static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal,
71  PTA **pptar);
72 static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1,
73  PTA **pptal2, PTA **pptar2);
74 static PTA *dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas);
75 static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h,
76  l_int32 *pntop, l_int32 *pnbot,
77  l_int32 *pytop, l_int32 *pybot);
78 static l_int32 dewarpLinearLSF(PTA *ptad, l_float32 *pa, l_float32 *pb,
79  l_float32 *pmederr);
80 static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb,
81  l_float32 *pc, l_float32 *pmederr);
82 static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew);
83 static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar,
84  l_uint32 color);
85 
86 
87 #ifndef NO_CONSOLE_IO
88 #define DEBUG_TEXTLINE_CENTERS 0 /* set this to 1 for debugging */
89 #define DEBUG_SHORT_LINES 0 /* ditto */
90 #else
91 #define DEBUG_TEXTLINE_CENTERS 0 /* always must be 0 */
92 #define DEBUG_SHORT_LINES 0 /* ditto */
93 #endif /* !NO_CONSOLE_IO */
94 
95  /* Special parameter values for reducing horizontal disparity */
96 static const l_float32 MinRatioLinesToHeight = 0.45;
97 static const l_int32 MinLinesForHoriz1 = 10; /* initially */
98 static const l_int32 MinLinesForHoriz2 = 3; /* after, in each half */
99 static const l_float32 AllowedWidthFract = 0.05; /* no bigger */
100 
101 
102 /*----------------------------------------------------------------------*
103  * Build basic page disparity model *
104  *----------------------------------------------------------------------*/
155 l_ok
157  const char *debugfile)
158 {
159 l_int32 linecount, ntop, nbot, ytop, ybot, ret;
160 PIX *pixs, *pix1, *pix2, *pix3;
161 PTA *pta;
162 PTAA *ptaa1, *ptaa2;
163 
164  if (!dew)
165  return ERROR_INT("dew not defined", __func__, 1);
166 
167  dew->debug = (debugfile) ? 1 : 0;
168  dew->vsuccess = dew->hsuccess = 0;
169  pixs = dew->pixs;
170  if (debugfile) {
171  lept_rmdir("lept/dewmod"); /* erase previous images */
172  lept_mkdir("lept/dewmod");
173  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
174  pixWriteDebug("/tmp/lept/dewmod/0010.png", pixs, IFF_PNG);
175  }
176 
177  /* Make initial estimate of centers of textlines */
178  ptaa1 = dewarpGetTextlineCenters(pixs, debugfile || DEBUG_TEXTLINE_CENTERS);
179  if (!ptaa1) {
180  L_WARNING("textline centers not found; model not built\n", __func__);
181  return 1;
182  }
183  if (debugfile) {
184  pix1 = pixConvertTo32(pixs);
185  pta = generatePtaFilledCircle(1);
186  pix2 = pixGenerateFromPta(pta, 5, 5);
187  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa1, pix2, 2, 2);
188  pixWriteDebug("/tmp/lept/dewmod/0020.png", pix3, IFF_PNG);
189  pixDestroy(&pix1);
190  pixDestroy(&pix2);
191  pixDestroy(&pix3);
192  ptaDestroy(&pta);
193  }
194 
195  /* Remove all lines that are not at least 0.8 times the length
196  * of the longest line. */
197  ptaa2 = dewarpRemoveShortLines(pixs, ptaa1, 0.8,
198  debugfile || DEBUG_SHORT_LINES);
199  if (debugfile) {
200  pix1 = pixConvertTo32(pixs);
201  pta = generatePtaFilledCircle(1);
202  pix2 = pixGenerateFromPta(pta, 5, 5);
203  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa2, pix2, 2, 2);
204  pixWriteDebug("/tmp/lept/dewmod/0030.png", pix3, IFF_PNG);
205  pixDestroy(&pix1);
206  pixDestroy(&pix2);
207  pixDestroy(&pix3);
208  ptaDestroy(&pta);
209  }
210  ptaaDestroy(&ptaa1);
211 
212  /* Verify that there are sufficient "long" lines */
213  linecount = ptaaGetCount(ptaa2);
214  if (linecount < dew->minlines) {
215  ptaaDestroy(&ptaa2);
216  L_WARNING("linecount %d < min req'd number of lines (%d) for model\n",
217  __func__, linecount, dew->minlines);
218  return 1;
219  }
220 
221  /* Verify that the lines have a reasonable coverage of the
222  * vertical extent of the page. */
223  if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs),
224  &ntop, &nbot, &ytop, &ybot) == FALSE) {
225  ptaaDestroy(&ptaa2);
226  L_WARNING("invalid line coverage: ntop = %d, nbot = %d;"
227  " spanning [%d ... %d] in height %d\n", __func__,
228  ntop, nbot, ytop, ybot, pixGetHeight(pixs));
229  return 1;
230  }
231 
232  /* Get the sampled vertical disparity from the textline centers.
233  * The disparity array will push pixels vertically so that each
234  * textline is flat and centered at the y-position of the mid-point. */
235  if (dewarpFindVertDisparity(dew, ptaa2, 0) != 0) {
236  L_WARNING("vertical disparity not built\n", __func__);
237  ptaaDestroy(&ptaa2);
238  return 1;
239  }
240 
241  /* Get the sampled horizontal disparity from the left and right
242  * edges of the text. The disparity array will expand the image
243  * linearly outward to align the text edges vertically.
244  * Do this even if useboth == 0; we still calculate it even
245  * if we don't plan to use it. */
246  if ((ret = dewarpFindHorizDisparity(dew, ptaa2)) == 0)
247  L_INFO("hsuccess = 1\n", __func__);
248 
249  /* Debug output */
250  if (debugfile) {
251  dewarpPopulateFullRes(dew, NULL, 0, 0);
252  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
253  pixWriteDebug("/tmp/lept/dewmod/0060.png", pix1, IFF_PNG);
254  pixDisplay(pix1, 1000, 0);
255  pixDestroy(&pix1);
256  if (ret == 0) {
257  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
258  pixWriteDebug("/tmp/lept/dewmod/0070.png", pix1, IFF_PNG);
259  pixDisplay(pix1, 1000, 0);
260  pixDestroy(&pix1);
261  }
262  convertFilesToPdf("/tmp/lept/dewmod", NULL, 135, 1.0, 0, 0,
263  "Dewarp Build Model", debugfile);
264  lept_stderr("pdf file: %s\n", debugfile);
265  }
266 
267  ptaaDestroy(&ptaa2);
268  return 0;
269 }
270 
271 
300 l_ok
302  PTAA *ptaa,
303  l_int32 rotflag)
304 {
305 l_int32 i, j, nlines, npts, nx, ny, sampling;
306 l_float32 c0, c1, c2, x, y, midy, val, medval, meddev, minval, maxval;
307 l_float32 *famidys;
308 NUMA *nax, *nafit, *nacurve0, *nacurve1, *nacurves;
309 NUMA *namidy, *namidys, *namidysi;
310 PIX *pix1, *pix2, *pixcirc, *pixdb;
311 PTA *pta, *ptad, *ptacirc;
312 PTAA *ptaa0, *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaat;
313 FPIX *fpix;
314 
315  if (!dew)
316  return ERROR_INT("dew not defined", __func__, 1);
317  dew->vsuccess = 0;
318  if (!ptaa)
319  return ERROR_INT("ptaa not defined", __func__, 1);
320 
321  if (dew->debug) L_INFO("finding vertical disparity\n", __func__);
322 
323  /* Do quadratic fit to smooth each line. A single quadratic
324  * over the entire width of the line appears to be sufficient.
325  * Quartics tend to overfit to noise. Each line is thus
326  * represented by three coefficients: y(x) = c2 * x^2 + c1 * x + c0.
327  * Using the coefficients, sample each fitted curve uniformly
328  * across the full width of the image. The result is in ptaa0. */
329  sampling = dew->sampling;
330  nx = (rotflag) ? dew->ny : dew->nx;
331  ny = (rotflag) ? dew->nx : dew->ny;
332  nlines = ptaaGetCount(ptaa);
333  dew->nlines = nlines;
334  ptaa0 = ptaaCreate(nlines);
335  nacurve0 = numaCreate(nlines); /* stores curvature coeff c2 */
336  pixdb = (rotflag) ? pixRotateOrth(dew->pixs, 1) : pixClone(dew->pixs);
337  for (i = 0; i < nlines; i++) { /* for each line */
338  pta = ptaaGetPta(ptaa, i, L_CLONE);
339  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
340  numaAddNumber(nacurve0, c2);
341  ptad = ptaCreate(nx);
342  for (j = 0; j < nx; j++) { /* uniformly sampled in x */
343  x = j * sampling;
344  applyQuadraticFit(c2, c1, c0, x, &y);
345  ptaAddPt(ptad, x, y);
346  }
347  ptaaAddPta(ptaa0, ptad, L_INSERT);
348  ptaDestroy(&pta);
349  }
350  if (dew->debug) {
351  lept_mkdir("lept/dewarp");
352  lept_mkdir("lept/dewdebug");
353  lept_mkdir("lept/dewmod");
354  ptaat = ptaaCreate(nlines);
355  for (i = 0; i < nlines; i++) {
356  pta = ptaaGetPta(ptaa, i, L_CLONE);
357  ptaGetArrays(pta, &nax, NULL);
358  ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit);
359  ptad = ptaCreateFromNuma(nax, nafit);
360  ptaaAddPta(ptaat, ptad, L_INSERT);
361  ptaDestroy(&pta);
362  numaDestroy(&nax);
363  numaDestroy(&nafit);
364  }
365  pix1 = pixConvertTo32(pixdb);
366  pta = generatePtaFilledCircle(1);
367  pixcirc = pixGenerateFromPta(pta, 5, 5);
368  pix2 = pixDisplayPtaaPattern(NULL, pix1, ptaat, pixcirc, 2, 2);
369  pixWriteDebug("/tmp/lept/dewmod/0041.png", pix2, IFF_PNG);
370  pixDestroy(&pix1);
371  pixDestroy(&pix2);
372  ptaDestroy(&pta);
373  pixDestroy(&pixcirc);
374  ptaaDestroy(&ptaat);
375  }
376 
377  /* Remove lines with outlier curvatures.
378  * Note that this is just looking for internal consistency in
379  * the line curvatures. It is not rejecting lines based on
380  * the magnitude of the curvature. That is done when constraints
381  * are applied for valid models. */
382  numaGetMedianDevFromMedian(nacurve0, &medval, &meddev);
383  L_INFO("\nPage %d\n", __func__, dew->pageno);
384  L_INFO("Pass 1: Curvature: medval = %f, meddev = %f\n",
385  __func__, medval, meddev);
386  ptaa1 = ptaaCreate(nlines);
387  nacurve1 = numaCreate(nlines);
388  for (i = 0; i < nlines; i++) { /* for each line */
389  numaGetFValue(nacurve0, i, &val);
390  if (L_ABS(val - medval) > 7.0 * meddev) /* TODO: reduce to ~ 3.0 */
391  continue;
392  pta = ptaaGetPta(ptaa0, i, L_CLONE);
393  ptaaAddPta(ptaa1, pta, L_INSERT);
394  numaAddNumber(nacurve1, val);
395  }
396  nlines = ptaaGetCount(ptaa1);
397  numaDestroy(&nacurve0);
398 
399  /* Save the min and max curvature (in micro-units) */
400  numaGetMin(nacurve1, &minval, NULL);
401  numaGetMax(nacurve1, &maxval, NULL);
402  dew->mincurv = lept_roundftoi(1000000. * minval);
403  dew->maxcurv = lept_roundftoi(1000000. * maxval);
404  L_INFO("Pass 2: Min/max curvature = (%d, %d)\n", __func__,
405  dew->mincurv, dew->maxcurv);
406 
407  /* Find and save the y values at the mid-points in each curve.
408  * If the slope is zero anywhere, it will typically be here. */
409  namidy = numaCreate(nlines);
410  for (i = 0; i < nlines; i++) {
411  pta = ptaaGetPta(ptaa1, i, L_CLONE);
412  npts = ptaGetCount(pta);
413  ptaGetPt(pta, npts / 2, NULL, &midy);
414  numaAddNumber(namidy, midy);
415  ptaDestroy(&pta);
416  }
417 
418  /* Sort the lines in ptaa1c by their vertical position, going down */
419  namidysi = numaGetSortIndex(namidy, L_SORT_INCREASING);
420  namidys = numaSortByIndex(namidy, namidysi);
421  nacurves = numaSortByIndex(nacurve1, namidysi);
422  numaDestroy(&dew->namidys); /* in case previously made */
423  numaDestroy(&dew->nacurves);
424  dew->namidys = namidys;
425  dew->nacurves = nacurves;
426  ptaa2 = ptaaSortByIndex(ptaa1, namidysi);
427  numaDestroy(&namidy);
428  numaDestroy(&nacurve1);
429  numaDestroy(&namidysi);
430  if (dew->debug) {
431  numaWriteDebug("/tmp/lept/dewdebug/midys.na", namidys);
432  numaWriteDebug("/tmp/lept/dewdebug/curves.na", nacurves);
433  pix1 = pixConvertTo32(pixdb);
434  ptacirc = generatePtaFilledCircle(5);
435  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
436  srand(3);
437  pixDisplayPtaaPattern(pix1, pix1, ptaa2, pixcirc, 5, 5);
438  srand(3); /* use the same colors for text and reference lines */
439  pixRenderMidYs(pix1, namidys, 2);
440  pix2 = (rotflag) ? pixRotateOrth(pix1, 3) : pixClone(pix1);
441  pixWriteDebug("/tmp/lept/dewmod/0042.png", pix2, IFF_PNG);
442  pixDisplay(pix2, 0, 0);
443  ptaDestroy(&ptacirc);
444  pixDestroy(&pixcirc);
445  pixDestroy(&pix1);
446  pixDestroy(&pix2);
447  }
448  pixDestroy(&pixdb);
449 
450  /* Convert the sampled points in ptaa2 to a sampled disparity with
451  * with respect to the y value at the mid point in the curve.
452  * The disparity is the distance the point needs to move;
453  * plus is downward. */
454  ptaa3 = ptaaCreate(nlines);
455  for (i = 0; i < nlines; i++) {
456  pta = ptaaGetPta(ptaa2, i, L_CLONE);
457  numaGetFValue(namidys, i, &midy);
458  ptad = ptaCreate(nx);
459  for (j = 0; j < nx; j++) {
460  ptaGetPt(pta, j, &x, &y);
461  ptaAddPt(ptad, x, midy - y);
462  }
463  ptaaAddPta(ptaa3, ptad, L_INSERT);
464  ptaDestroy(&pta);
465  }
466  if (dew->debug) {
467  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa3.ptaa", ptaa3, 0);
468  }
469 
470  /* Generate ptaa4 by taking vertical 'columns' from ptaa3.
471  * We want to fit the vertical disparity on the column to the
472  * vertical position of the line, which we call 'y' here and
473  * obtain from namidys. So each pta in ptaa4 is the set of
474  * vertical disparities down a column of points. The columns
475  * in ptaa4 are equally spaced in x. */
476  ptaa4 = ptaaCreate(nx);
477  famidys = numaGetFArray(namidys, L_NOCOPY);
478  for (j = 0; j < nx; j++) {
479  pta = ptaCreate(nlines);
480  for (i = 0; i < nlines; i++) {
481  y = famidys[i];
482  ptaaGetPt(ptaa3, i, j, NULL, &val); /* disparity value */
483  ptaAddPt(pta, y, val);
484  }
485  ptaaAddPta(ptaa4, pta, L_INSERT);
486  }
487  if (dew->debug) {
488  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa4.ptaa", ptaa4, 0);
489  }
490 
491  /* Do quadratic fit vertically on each of the pixel columns
492  * in ptaa4, for the vertical displacement (which identifies the
493  * src pixel(s) for each dest pixel) as a function of y (the
494  * y value of the mid-points for each line). Then generate
495  * ptaa5 by sampling the fitted vertical displacement on a
496  * regular grid in the vertical direction. Each pta in ptaa5
497  * gives the vertical displacement for regularly sampled y values
498  * at a fixed x. */
499  ptaa5 = ptaaCreate(nx); /* uniformly sampled across full height of image */
500  for (j = 0; j < nx; j++) { /* for each column */
501  pta = ptaaGetPta(ptaa4, j, L_CLONE);
502  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
503  ptad = ptaCreate(ny);
504  for (i = 0; i < ny; i++) { /* uniformly sampled in y */
505  y = i * sampling;
506  applyQuadraticFit(c2, c1, c0, y, &val);
507  ptaAddPt(ptad, y, val);
508  }
509  ptaaAddPta(ptaa5, ptad, L_INSERT);
510  ptaDestroy(&pta);
511  }
512  if (dew->debug) {
513  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa5.ptaa", ptaa5, 0);
514  convertFilesToPdf("/tmp/lept/dewmod", "004", 135, 1.0, 0, 0,
515  "Dewarp Vert Disparity",
516  "/tmp/lept/dewarp/vert_disparity.pdf");
517  lept_stderr("pdf file: /tmp/lept/dewarp/vert_disparity.pdf\n");
518  }
519 
520  /* Save the result in a fpix at the specified subsampling */
521  fpix = fpixCreate(nx, ny);
522  for (i = 0; i < ny; i++) {
523  for (j = 0; j < nx; j++) {
524  ptaaGetPt(ptaa5, j, i, NULL, &val);
525  fpixSetPixel(fpix, j, i, val);
526  }
527  }
528  dew->sampvdispar = fpix;
529  dew->vsuccess = 1;
530 
531  ptaaDestroy(&ptaa0);
532  ptaaDestroy(&ptaa1);
533  ptaaDestroy(&ptaa2);
534  ptaaDestroy(&ptaa3);
535  ptaaDestroy(&ptaa4);
536  ptaaDestroy(&ptaa5);
537  return 0;
538 }
539 
540 
562 l_ok
564  PTAA *ptaa)
565 {
566 l_int32 i, j, h, nx, ny, sampling, ret, linear_edgefit;
567 l_float32 c0, c1, cl0, cl1, cl2, cr0, cr1, cr2;
568 l_float32 x, y, refl, refr;
569 l_float32 val, mederr;
570 NUMA *nald, *nard;
571 PIX *pix1;
572 PTA *ptal1, *ptar1; /* left/right end points of lines; initial */
573 PTA *ptal2, *ptar2; /* left/right end points; after filtering */
574 PTA *ptal3, *ptar3; /* left and right block, fitted, uniform spacing */
575 PTA *pta, *ptat, *pta1, *pta2;
576 PTAA *ptaah;
577 FPIX *fpix;
578 
579  if (!dew)
580  return ERROR_INT("dew not defined", __func__, 1);
581  dew->hsuccess = 0;
582  if (!ptaa)
583  return ERROR_INT("ptaa not defined", __func__, 1);
584 
585  if (dew->debug) L_INFO("finding horizontal disparity\n", __func__);
586 
587  /* Get the endpoints of the lines, and sort from top to bottom */
588  h = pixGetHeight(dew->pixs);
589  ret = dewarpGetLineEndPoints(h, ptaa, &ptal1, &ptar1);
590  if (ret) {
591  L_INFO("Horiz disparity not built\n", __func__);
592  return 1;
593  }
594  if (dew->debug) {
595  lept_mkdir("lept/dewdebug");
596  lept_mkdir("lept/dewarp");
597  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left1.pta", ptal1, 1);
598  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right1.pta", ptar1, 1);
599  }
600 
601  /* Filter the points by x-location to prevent 2-column images
602  * from getting confused about left and right endpoints. We
603  * require valid left points to not be farther than
604  * 0.20 * (remaining distance to the right edge of the image)
605  * to the right of the leftmost endpoint, and similarly for
606  * the right endpoints. (Note: x and y are reversed in the pta.)
607  * Also require end points to be near the medians in the
608  * upper and lower halves. */
609  ret = dewarpFilterLineEndPoints(dew, ptal1, ptar1, &ptal2, &ptar2);
610  ptaDestroy(&ptal1);
611  ptaDestroy(&ptar1);
612  if (ret) {
613  L_INFO("Not enough filtered end points\n", __func__);
614  return 1;
615  }
616 
617  /* Do either a linear or a quadratic fit to the left and right
618  * endpoints of the longest lines. It is not necessary to use
619  * the noisy LSF fit function, because we've removed outlier
620  * end points by selecting the long lines.
621  * For the linear fit, each line is represented by 2 coefficients:
622  * x(y) = c1 * y + c0.
623  * For the quadratic fit, each line is represented by 3 coefficients:
624  * x(y) = c2 * y^2 + c1 * y + c0.
625  * Then using the coefficients, sample each fitted curve uniformly
626  * along the full height of the image. */
627  sampling = dew->sampling;
628  nx = dew->nx;
629  ny = dew->ny;
630  linear_edgefit = (dew->dewa->max_edgecurv == 0) ? TRUE : FALSE;
631 
632  if (linear_edgefit) {
633  /* Fit the left side, using linear LSF on the set of long lines. */
634  dewarpLinearLSF(ptal2, &cl1, &cl0, &mederr);
635  dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */
636  dew->leftcurv = 0; /* micro-units */
637  L_INFO("Left linear LSF median error = %5.2f\n", __func__, mederr);
638  L_INFO("Left edge slope = %d\n", __func__, dew->leftslope);
639  ptal3 = ptaCreate(ny);
640  for (i = 0; i < ny; i++) { /* uniformly sample in y */
641  y = i * sampling;
642  applyLinearFit(cl1, cl0, y, &x);
643  ptaAddPt(ptal3, x, y);
644  }
645 
646  /* Do a linear LSF on the right side. */
647  dewarpLinearLSF(ptar2, &cr1, &cr0, &mederr);
648  dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */
649  dew->rightcurv = 0; /* micro-units */
650  L_INFO("Right linear LSF median error = %5.2f\n", __func__, mederr);
651  L_INFO("Right edge slope = %d\n", __func__, dew->rightslope);
652  ptar3 = ptaCreate(ny);
653  for (i = 0; i < ny; i++) { /* uniformly sample in y */
654  y = i * sampling;
655  applyLinearFit(cr1, cr0, y, &x);
656  ptaAddPt(ptar3, x, y);
657  }
658  } else { /* quadratic edge fit */
659  /* Fit the left side, using quadratic LSF on the long lines. */
660  dewarpQuadraticLSF(ptal2, &cl2, &cl1, &cl0, &mederr);
661  dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */
662  dew->leftcurv = lept_roundftoi(1000000. * cl2); /* micro-units */
663  L_INFO("Left quad LSF median error = %5.2f\n", __func__, mederr);
664  L_INFO("Left edge slope = %d\n", __func__, dew->leftslope);
665  L_INFO("Left edge curvature = %d\n", __func__, dew->leftcurv);
666  ptal3 = ptaCreate(ny);
667  for (i = 0; i < ny; i++) { /* uniformly sample in y */
668  y = i * sampling;
669  applyQuadraticFit(cl2, cl1, cl0, y, &x);
670  ptaAddPt(ptal3, x, y);
671  }
672 
673  /* Do a quadratic LSF on the right side. */
674  dewarpQuadraticLSF(ptar2, &cr2, &cr1, &cr0, &mederr);
675  dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */
676  dew->rightcurv = lept_roundftoi(1000000. * cr2); /* micro-units */
677  L_INFO("Right quad LSF median error = %5.2f\n", __func__, mederr);
678  L_INFO("Right edge slope = %d\n", __func__, dew->rightslope);
679  L_INFO("Right edge curvature = %d\n", __func__, dew->rightcurv);
680  ptar3 = ptaCreate(ny);
681  for (i = 0; i < ny; i++) { /* uniformly sample in y */
682  y = i * sampling;
683  applyQuadraticFit(cr2, cr1, cr0, y, &x);
684  ptaAddPt(ptar3, x, y);
685  }
686  }
687 
688  if (dew->debug) {
689  PTA *ptalft, *ptarft;
690  h = pixGetHeight(dew->pixs);
691  pta1 = ptaCreate(h);
692  pta2 = ptaCreate(h);
693  if (linear_edgefit) {
694  for (i = 0; i < h; i++) {
695  applyLinearFit(cl1, cl0, i, &x);
696  ptaAddPt(pta1, x, i);
697  applyLinearFit(cr1, cr0, i, &x);
698  ptaAddPt(pta2, x, i);
699  }
700  } else { /* quadratic edge fit */
701  for (i = 0; i < h; i++) {
702  applyQuadraticFit(cl2, cl1, cl0, i, &x);
703  ptaAddPt(pta1, x, i);
704  applyQuadraticFit(cr2, cr1, cr0, i, &x);
705  ptaAddPt(pta2, x, i);
706  }
707  }
708  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
709  pixDisplayPta(pix1, pix1, pta2);
710  pixRenderHorizEndPoints(pix1, ptal2, ptar2, 0xff000000);
711  pixDisplay(pix1, 600, 800);
712  pixWriteDebug("/tmp/lept/dewmod/0051.png", pix1, IFF_PNG);
713  pixDestroy(&pix1);
714 
715  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
716  pixDisplayPta(pix1, pix1, pta2);
717  ptalft = ptaTranspose(ptal3);
718  ptarft = ptaTranspose(ptar3);
719  pixRenderHorizEndPoints(pix1, ptalft, ptarft, 0x0000ff00);
720  pixDisplay(pix1, 800, 800);
721  pixWriteDebug("/tmp/lept/dewmod/0052.png", pix1, IFF_PNG);
722  convertFilesToPdf("/tmp/lept/dewmod", "005", 135, 1.0, 0, 0,
723  "Dewarp Horiz Disparity",
724  "/tmp/lept/dewarp/horiz_disparity.pdf");
725  lept_stderr("pdf file: /tmp/lept/dewarp/horiz_disparity.pdf\n");
726  pixDestroy(&pix1);
727  ptaDestroy(&pta1);
728  ptaDestroy(&pta2);
729  ptaDestroy(&ptalft);
730  ptaDestroy(&ptarft);
731  }
732 
733  /* Find the x value at the midpoints (in y) of the two vertical lines,
734  * ptal3 and ptar3. These are the reference values for each of the
735  * lines. Then use the difference between the these midpoint
736  * values and the actual x coordinates of the lines to represent
737  * the horizontal disparity (nald, nard) on the vertical lines
738  * for the sampled y values. */
739  ptaGetPt(ptal3, ny / 2, &refl, NULL);
740  ptaGetPt(ptar3, ny / 2, &refr, NULL);
741  nald = numaCreate(ny);
742  nard = numaCreate(ny);
743  for (i = 0; i < ny; i++) {
744  ptaGetPt(ptal3, i, &x, NULL);
745  numaAddNumber(nald, refl - x);
746  ptaGetPt(ptar3, i, &x, NULL);
747  numaAddNumber(nard, refr - x);
748  }
749 
750  /* Now for each pair of sampled values of the two lines (at the
751  * same value of y), do a linear interpolation to generate
752  * the horizontal disparity on all sampled points between them. */
753  ptaah = ptaaCreate(ny);
754  for (i = 0; i < ny; i++) {
755  pta = ptaCreate(2);
756  numaGetFValue(nald, i, &val);
757  ptaAddPt(pta, refl, val);
758  numaGetFValue(nard, i, &val);
759  ptaAddPt(pta, refr, val);
760  ptaGetLinearLSF(pta, &c1, &c0, NULL); /* horiz disparity along line */
761  ptat = ptaCreate(nx);
762  for (j = 0; j < nx; j++) {
763  x = j * sampling;
764  applyLinearFit(c1, c0, x, &val);
765  ptaAddPt(ptat, x, val);
766  }
767  ptaaAddPta(ptaah, ptat, L_INSERT);
768  ptaDestroy(&pta);
769  }
770  numaDestroy(&nald);
771  numaDestroy(&nard);
772 
773  /* Save the result in a fpix at the specified subsampling */
774  fpix = fpixCreate(nx, ny);
775  for (i = 0; i < ny; i++) {
776  for (j = 0; j < nx; j++) {
777  ptaaGetPt(ptaah, i, j, NULL, &val);
778  fpixSetPixel(fpix, j, i, val);
779  }
780  }
781  dew->samphdispar = fpix;
782  dew->hsuccess = 1;
783  ptaDestroy(&ptal2);
784  ptaDestroy(&ptar2);
785  ptaDestroy(&ptal3);
786  ptaDestroy(&ptar3);
787  ptaaDestroy(&ptaah);
788  return 0;
789 }
790 
791 
807 PTAA *
809  l_int32 debugflag)
810 {
811 char buf[64];
812 l_int32 i, w, h, bx, by, nsegs, csize1, csize2;
813 BOXA *boxa;
814 PIX *pix1, *pix2;
815 PIXA *pixa1, *pixa2;
816 PTA *pta;
817 PTAA *ptaa;
818 
819  if (!pixs || pixGetDepth(pixs) != 1)
820  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
821  pixGetDimensions(pixs, &w, &h, NULL);
822 
823  if (debugflag) L_INFO("finding text line centers\n", __func__);
824 
825  /* Filter to solidify the text lines within the x-height region,
826  * and to remove most of the ascenders and descenders.
827  * We start with a small vertical opening to remove noise beyond
828  * the line that can cause an error in the line end points.
829  * The small closing (csize1) is used to bridge the gaps between
830  * letters. The large closing (csize2) bridges the gaps between
831  * words; using 1/30 of the page width usually suffices. */
832  csize1 = L_MAX(15, w / 80);
833  csize2 = L_MAX(40, w / 30);
834  snprintf(buf, sizeof(buf), "o1.3 + c%d.1 + o%d.1 + c%d.1",
835  csize1, csize1, csize2);
836  pix1 = pixMorphSequence(pixs, buf, 0);
837 
838  /* Remove the components (e.g., embedded images) that have
839  * long vertical runs (>= 50 pixels). You can't use bounding
840  * boxes because connected component b.b. of lines can be quite
841  * tall due to slope and curvature. */
842  pix2 = pixMorphSequence(pix1, "e1.50", 0); /* seed */
843  pixSeedfillBinary(pix2, pix2, pix1, 8); /* tall components */
844  pixXor(pix2, pix2, pix1); /* remove tall */
845 
846  if (debugflag) {
847  lept_mkdir("lept/dewmod");
848  pixWriteDebug("/tmp/lept/dewmod/0011.tif", pix1, IFF_TIFF_G4);
849  pixDisplayWithTitle(pix1, 0, 600, "pix1", 1);
850  pixWriteDebug("/tmp/lept/dewmod/0012.tif", pix2, IFF_TIFF_G4);
851  pixDisplayWithTitle(pix2, 0, 800, "pix2", 1);
852  }
853  pixDestroy(&pix1);
854 
855  /* Get the 8-connected components ... */
856  boxa = pixConnComp(pix2, &pixa1, 8);
857  pixDestroy(&pix2);
858  boxaDestroy(&boxa);
859  if (pixaGetCount(pixa1) == 0) {
860  pixaDestroy(&pixa1);
861  return NULL;
862  }
863 
864  /* ... and remove the short width and very short height c.c */
865  pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH,
866  L_SELECT_IF_GT, NULL);
867  if ((nsegs = pixaGetCount(pixa2)) == 0) {
868  pixaDestroy(&pixa1);
869  pixaDestroy(&pixa2);
870  return NULL;
871  }
872  if (debugflag) {
873  pix2 = pixaDisplay(pixa2, w, h);
874  pixWriteDebug("/tmp/lept/dewmod/0013.tif", pix2, IFF_TIFF_G4);
875  pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1);
876  pixDestroy(&pix2);
877  }
878 
879  /* For each c.c., get the weighted center of each vertical column.
880  * The result is a set of points going approximately through
881  * the center of the x-height part of the text line. */
882  ptaa = ptaaCreate(nsegs);
883  for (i = 0; i < nsegs; i++) {
884  pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL);
885  pix2 = pixaGetPix(pixa2, i, L_CLONE);
886  pta = dewarpGetMeanVerticals(pix2, bx, by);
887  ptaaAddPta(ptaa, pta, L_INSERT);
888  pixDestroy(&pix2);
889  }
890  if (debugflag) {
891  pix1 = pixCreateTemplate(pixs);
892  pix2 = pixDisplayPtaa(pix1, ptaa);
893  pixWriteDebug("/tmp/lept/dewmod/0014.tif", pix2, IFF_PNG);
894  pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1);
895  pixDestroy(&pix1);
896  pixDestroy(&pix2);
897  }
898 
899  pixaDestroy(&pixa1);
900  pixaDestroy(&pixa2);
901  return ptaa;
902 }
903 
904 
913 static PTA *
915  l_int32 x,
916  l_int32 y)
917 {
918 l_int32 w, h, i, j, wpl, sum, count;
919 l_uint32 *line, *data;
920 PTA *pta;
921 
922  if (!pixs || pixGetDepth(pixs) != 1)
923  return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
924 
925  pixGetDimensions(pixs, &w, &h, NULL);
926  pta = ptaCreate(w);
927  data = pixGetData(pixs);
928  wpl = pixGetWpl(pixs);
929  for (j = 0; j < w; j++) {
930  line = data;
931  sum = count = 0;
932  for (i = 0; i < h; i++) {
933  if (GET_DATA_BIT(line, j) == 1) {
934  sum += i;
935  count += 1;
936  }
937  line += wpl;
938  }
939  if (count == 0) continue;
940  ptaAddPt(pta, x + j, y + (sum / count));
941  }
942 
943  return pta;
944 }
945 
946 
957 PTAA *
959  PTAA *ptaas,
960  l_float32 fract,
961  l_int32 debugflag)
962 {
963 l_int32 w, n, i, index, maxlen, len;
964 l_float32 minx, maxx;
965 NUMA *na, *naindex;
966 PIX *pix1, *pix2;
967 PTA *pta;
968 PTAA *ptaad;
969 
970  if (!pixs || pixGetDepth(pixs) != 1)
971  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
972  if (!ptaas)
973  return (PTAA *)ERROR_PTR("ptaas undefined", __func__, NULL);
974 
975  pixGetDimensions(pixs, &w, NULL, NULL);
976  n = ptaaGetCount(ptaas);
977  ptaad = ptaaCreate(n);
978  na = numaCreate(n);
979  for (i = 0; i < n; i++) {
980  pta = ptaaGetPta(ptaas, i, L_CLONE);
981  ptaGetRange(pta, &minx, &maxx, NULL, NULL);
982  numaAddNumber(na, maxx - minx + 1);
983  ptaDestroy(&pta);
984  }
985 
986  /* Sort by length and find all that are long enough */
987  naindex = numaGetSortIndex(na, L_SORT_DECREASING);
988  numaGetIValue(naindex, 0, &index);
989  numaGetIValue(na, index, &maxlen);
990  if (maxlen < 0.5 * w)
991  L_WARNING("lines are relatively short\n", __func__);
992  pta = ptaaGetPta(ptaas, index, L_CLONE);
993  ptaaAddPta(ptaad, pta, L_INSERT);
994  for (i = 1; i < n; i++) {
995  numaGetIValue(naindex, i, &index);
996  numaGetIValue(na, index, &len);
997  if (len < fract * maxlen) break;
998  pta = ptaaGetPta(ptaas, index, L_CLONE);
999  ptaaAddPta(ptaad, pta, L_INSERT);
1000  }
1001 
1002  if (debugflag) {
1003  pix1 = pixCopy(NULL, pixs);
1004  pix2 = pixDisplayPtaa(pix1, ptaad);
1005  pixDisplayWithTitle(pix2, 0, 200, "pix4", 1);
1006  pixDestroy(&pix1);
1007  pixDestroy(&pix2);
1008  }
1009 
1010  numaDestroy(&na);
1011  numaDestroy(&naindex);
1012  return ptaad;
1013 }
1014 
1015 
1038 static l_int32
1040  PTAA *ptaa,
1041  PTA **pptal,
1042  PTA **pptar)
1043 {
1044 l_int32 i, n, npt, x, y;
1045 l_float32 miny, maxy, ratio;
1046 PTA *pta, *ptal1, *ptar1;
1047 
1048  if (!pptal || !pptar)
1049  return ERROR_INT("&ptal and &ptar not both defined", __func__, 1);
1050  *pptal = *pptar = NULL;
1051  if (!ptaa)
1052  return ERROR_INT("ptaa undefined", __func__, 1);
1053 
1054  /* Are there at least 10 lines? */
1055  n = ptaaGetCount(ptaa);
1056  if (n < MinLinesForHoriz1) {
1057  L_INFO("only %d lines; too few\n", __func__, n);
1058  return 1;
1059  }
1060 
1061  /* Extract the line end points, and transpose x and y values */
1062  ptal1 = ptaCreate(n);
1063  ptar1 = ptaCreate(n);
1064  for (i = 0; i < n; i++) {
1065  pta = ptaaGetPta(ptaa, i, L_CLONE);
1066  ptaGetIPt(pta, 0, &x, &y);
1067  ptaAddPt(ptal1, y, x); /* transpose */
1068  npt = ptaGetCount(pta);
1069  ptaGetIPt(pta, npt - 1, &x, &y);
1070  ptaAddPt(ptar1, y, x); /* transpose */
1071  ptaDestroy(&pta);
1072  }
1073 
1074  /* Use the min and max of the y value on the left side. */
1075  ptaGetRange(ptal1, &miny, &maxy, NULL, NULL);
1076  ratio = (maxy - miny) / (l_float32)h;
1077  if (ratio < MinRatioLinesToHeight) {
1078  L_INFO("ratio lines to height, %f, too small\n", __func__, ratio);
1079  ptaDestroy(&ptal1);
1080  ptaDestroy(&ptar1);
1081  return 1;
1082  }
1083 
1084  /* Sort from top to bottom */
1085  *pptal = ptaSort(ptal1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1086  *pptar = ptaSort(ptar1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1087  ptaDestroy(&ptal1);
1088  ptaDestroy(&ptar1);
1089  return 0;
1090 }
1091 
1092 
1115 static l_int32
1117  PTA *ptal,
1118  PTA *ptar,
1119  PTA **pptalf,
1120  PTA **pptarf)
1121 {
1122 l_int32 w, i, n;
1123 l_float32 ymin, ymax, xvall, xvalr, yvall, yvalr;
1124 PTA *ptal1, *ptar1, *ptal2, *ptar2;
1125 
1126  if (!ptal || !ptar)
1127  return ERROR_INT("ptal or ptar not defined", __func__, 1);
1128  *pptalf = *pptarf = NULL;
1129 
1130  /* First filter for lines near left and right margins */
1131  w = pixGetWidth(dew->pixs);
1132  ptaGetMinMax(ptal, NULL, &ymin, NULL, NULL);
1133  ptaGetMinMax(ptar, NULL, NULL, NULL, &ymax);
1134  n = ptaGetCount(ptal); /* ptar is the same size; at least 10 */
1135  ptal1 = ptaCreate(n);
1136  ptar1 = ptaCreate(n);
1137  for (i = 0; i < n; i++) {
1138  ptaGetPt(ptal, i, &xvall, &yvall);
1139  ptaGetPt(ptar, i, &xvalr, &yvalr);
1140  if (yvall < ymin + 0.20 * (w - ymin) &&
1141  yvalr > 0.80 * ymax) {
1142  ptaAddPt(ptal1, xvall, yvall);
1143  ptaAddPt(ptar1, xvalr, yvalr);
1144  }
1145  }
1146  if (dew->debug) {
1147  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left2.pta", ptal1, 1);
1148  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right2.pta", ptar1, 1);
1149  }
1150 
1151  n = L_MIN(ptaGetCount(ptal1), ptaGetCount(ptar1));
1152  if (n < MinLinesForHoriz1 - 2) {
1153  ptaDestroy(&ptal1);
1154  ptaDestroy(&ptar1);
1155  L_INFO("First filter: only %d endpoints; needed 8\n", __func__, n);
1156  return 1;
1157  }
1158 
1159  /* Remove outlier points */
1160  ptal2 = dewarpRemoveBadEndPoints(w, ptal1);
1161  ptar2 = dewarpRemoveBadEndPoints(w, ptar1);
1162  ptaDestroy(&ptal1);
1163  ptaDestroy(&ptar1);
1164  if (!ptal2 || !ptar2) {
1165  ptaDestroy(&ptal2);
1166  ptaDestroy(&ptar2);
1167  L_INFO("Second filter: too few endpoints left after outliers removed\n",
1168  __func__);
1169  return 1;
1170  }
1171  if (dew->debug) {
1172  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left3.pta", ptal2, 1);
1173  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right3.pta", ptar2, 1);
1174  }
1175 
1176  *pptalf = ptal2;
1177  *pptarf = ptar2;
1178  return 0;
1179 }
1180 
1181 
1200 static PTA *
1202  PTA *ptas)
1203 {
1204 l_int32 i, n, nu, nd;
1205 l_float32 rval, xval, yval, delta;
1206 PTA *ptau1, *ptau2, *ptad1, *ptad2;
1207 
1208  if (!ptas)
1209  return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL);
1210 
1211  delta = AllowedWidthFract * w;
1212  n = ptaGetCount(ptas); /* will be at least 8 */
1213 
1214  /* Check the upper half */
1215  ptau1 = ptaSelectRange(ptas, 0, n / 2);
1216  ptaGetRankValue(ptau1, 0.5, NULL, L_SORT_BY_Y, &rval);
1217  nu = ptaGetCount(ptau1);
1218  ptau2 = ptaCreate(nu);
1219  for (i = 0; i < nu; i++) {
1220  ptaGetPt(ptau1, i, &xval, &yval); /* transposed */
1221  if (L_ABS(rval - yval) <= delta)
1222  ptaAddPt(ptau2, xval, yval);
1223  }
1224  ptaDestroy(&ptau1);
1225  if (ptaGetCount(ptau2) < MinLinesForHoriz2) {
1226  ptaDestroy(&ptau2);
1227  L_INFO("Second filter: upper set is too small after outliers removed\n",
1228  __func__);
1229  return NULL;
1230  }
1231 
1232  /* Check the lower half */
1233  ptad1 = ptaSelectRange(ptas, n / 2 + 1, -1);
1234  ptaGetRankValue(ptad1, 0.5, NULL, L_SORT_BY_Y, &rval);
1235  nd = ptaGetCount(ptad1);
1236  ptad2 = ptaCreate(nd);
1237  for (i = 0; i < nd; i++) {
1238  ptaGetPt(ptad1, i, &xval, &yval); /* transposed */
1239  if (L_ABS(rval - yval) <= delta)
1240  ptaAddPt(ptad2, xval, yval);
1241  }
1242  ptaDestroy(&ptad1);
1243  if (ptaGetCount(ptad2) < MinLinesForHoriz2) {
1244  ptaDestroy(&ptau2);
1245  ptaDestroy(&ptad2);
1246  L_INFO("Second filter: lower set is too small after outliers removed\n",
1247  __func__);
1248  return NULL;
1249  }
1250 
1251  ptaJoin(ptau2, ptad2, 0, -1);
1252  ptaDestroy(&ptad2);
1253  return ptau2;
1254 }
1255 
1256 
1276 static l_int32
1278  l_int32 h,
1279  l_int32 *pntop,
1280  l_int32 *pnbot,
1281  l_int32 *pytop,
1282  l_int32 *pybot)
1283 {
1284 l_int32 i, n, iy, both_halves, ntop, nbot, ytop, ybot, nmin;
1285 l_float32 y, fraction;
1286 NUMA *na;
1287 
1288  if (!ptaa)
1289  return ERROR_INT("ptaa not defined", __func__, 0);
1290  if ((n = ptaaGetCount(ptaa)) == 0)
1291  return ERROR_INT("ptaa empty", __func__, 0);
1292  if (h <= 0)
1293  return ERROR_INT("invalid h", __func__, 0);
1294  if (!pntop || !pnbot)
1295  return ERROR_INT("&ntop and &nbot not defined", __func__, 0);
1296  if (!pytop || !pybot)
1297  return ERROR_INT("&ytop and &ybot not defined", __func__, 0);
1298 
1299  na = numaCreate(n);
1300  for (i = 0; i < n; i++) {
1301  ptaaGetPt(ptaa, i, 0, NULL, &y);
1302  numaAddNumber(na, y);
1303  }
1304  numaSort(na, na, L_SORT_INCREASING);
1305  for (i = 0, ntop = 0; i < n; i++) {
1306  numaGetIValue(na, i, &iy);
1307  if (i == 0) ytop = iy;
1308  if (i == n - 1) ybot = iy;
1309  if (iy < 0.5 * h)
1310  ntop++;
1311  }
1312  numaDestroy(&na);
1313  nbot = n - ntop;
1314  *pntop = ntop;
1315  *pnbot = nbot;
1316  *pytop = ytop;
1317  *pybot = ybot;
1318  nmin = 4; /* minimum number of lines required in each half */
1319  both_halves = (ntop >= nmin) && (nbot >= nmin);
1320  fraction = (l_float32)(ybot - ytop) / (l_float32)h;
1321  if (both_halves && fraction > 0.50)
1322  return 1;
1323  return 0;
1324 }
1325 
1326 
1344 static l_int32
1346  l_float32 *pa,
1347  l_float32 *pb,
1348  l_float32 *pmederr)
1349 {
1350 l_int32 i, n;
1351 l_float32 x, y, xp, c0, c1;
1352 NUMA *naerr;
1353 
1354  if (pmederr) *pmederr = 0.0;
1355  if (!pa || !pb)
1356  return ERROR_INT("not all ptrs are defined", __func__, 1);
1357  *pa = *pb = 0.0;
1358  if (!ptad)
1359  return ERROR_INT("ptad not defined", __func__, 1);
1360 
1361  /* Fit to the longest lines */
1362  ptaGetLinearLSF(ptad, &c1, &c0, NULL);
1363  *pa = c1;
1364  *pb = c0;
1365 
1366  /* Optionally, find the median error */
1367  if (pmederr) {
1368  n = ptaGetCount(ptad);
1369  naerr = numaCreate(n);
1370  for (i = 0; i < n; i++) {
1371  ptaGetPt(ptad, i, &y, &xp);
1372  applyLinearFit(c1, c0, y, &x);
1373  numaAddNumber(naerr, L_ABS(x - xp));
1374  }
1375  numaGetMedian(naerr, pmederr);
1376  numaDestroy(&naerr);
1377  }
1378  return 0;
1379 }
1380 
1381 
1400 static l_int32
1402  l_float32 *pa,
1403  l_float32 *pb,
1404  l_float32 *pc,
1405  l_float32 *pmederr)
1406 {
1407 l_int32 i, n;
1408 l_float32 x, y, xp, c0, c1, c2;
1409 NUMA *naerr;
1410 
1411  if (pmederr) *pmederr = 0.0;
1412  if (!pa || !pb || !pc)
1413  return ERROR_INT("not all ptrs are defined", __func__, 1);
1414  *pa = *pb = *pc = 0.0;
1415  if (!ptad)
1416  return ERROR_INT("ptad not defined", __func__, 1);
1417 
1418  /* Fit to the longest lines */
1419  ptaGetQuadraticLSF(ptad, &c2, &c1, &c0, NULL);
1420  *pa = c2;
1421  *pb = c1;
1422  *pc = c0;
1423 
1424  /* Optionally, find the median error */
1425  if (pmederr) {
1426  n = ptaGetCount(ptad);
1427  naerr = numaCreate(n);
1428  for (i = 0; i < n; i++) {
1429  ptaGetPt(ptad, i, &y, &xp);
1430  applyQuadraticFit(c2, c1, c0, y, &x);
1431  numaAddNumber(naerr, L_ABS(x - xp));
1432  }
1433  numaGetMedian(naerr, pmederr);
1434  numaDestroy(&naerr);
1435  }
1436  return 0;
1437 }
1438 
1439 
1440 /*----------------------------------------------------------------------*
1441  * Build disparity model for slope near binding *
1442  *----------------------------------------------------------------------*/
1481 l_ok
1483  PIX *pixb,
1484  l_float32 fractthresh,
1485  l_int32 parity)
1486 {
1487 l_int32 i, j, x, n1, n2, nb, ne, count, w, h, ival, prev;
1488 l_int32 istart, iend, first, last, x0, x1, nx, ny;
1489 l_float32 fract, delta, sum, aveval, fval, del, denom;
1490 l_float32 ca, cb, cc, cd, ce, y;
1491 BOX *box;
1492 BOXA *boxa1, *boxa2;
1493 GPLOT *gplot;
1494 NUMA *na1, *na2, *na3, *na4, *nasum;
1495 PIX *pix1;
1496 PTA *pta1;
1497 FPIX *fpix;
1498 
1499  if (!dew)
1500  return ERROR_INT("dew not defined", __func__, 1);
1501  if (!dew->vvalid || !dew->hvalid)
1502  return ERROR_INT("invalid vert or horiz disparity model", __func__, 1);
1503  if (!pixb || pixGetDepth(pixb) != 1)
1504  return ERROR_INT("pixb not defined or not 1 bpp", __func__, 1);
1505 
1506  if (dew->debug) L_INFO("finding slope horizontal disparity\n", __func__);
1507 
1508  /* Find the bounding boxes of the vertical strokes; remove noise */
1509  pix1 = pixMorphSequence(pixb, "o1.10", 0);
1510  pixDisplay(pix1, 100, 100);
1511  boxa1 = pixConnCompBB(pix1, 4);
1512  boxa2 = boxaSelectBySize(boxa1, 0, 5, L_SELECT_HEIGHT, L_SELECT_IF_GT,
1513  NULL);
1514  nb = boxaGetCount(boxa2);
1515  lept_stderr("number of components: %d\n", nb);
1516  boxaDestroy(&boxa1);
1517 
1518  /* Estimate the horizontal density of vertical strokes */
1519  na1 = numaCreate(0);
1520  numaSetParameters(na1, 0, 25);
1521  pixGetDimensions(pixb, &w, &h, NULL);
1522  for (x = 0; x + 50 < w; x += 25) {
1523  box = boxCreate(x, 0, 50, h);
1524  boxaContainedInBoxCount(boxa2, box, &count);
1525  numaAddNumber(na1, count);
1526  boxDestroy(&box);
1527  }
1528  if (dew->debug) {
1529  lept_mkdir("lept/dew");
1530  gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/dew/0091", NULL);
1531  lept_mv("/tmp/lept/dew/0091.png", "lept/dewmod", NULL, NULL);
1532  pixWriteDebug("/tmp/lept/dewmod/0090.png", pix1, IFF_PNG);
1533  }
1534  pixDestroy(&pix1);
1535  boxaDestroy(&boxa2);
1536 
1537  /* Find the left and right end local maxima; if the difference
1538  * is small, quit. */
1539  n1 = numaGetCount(na1);
1540  prev = 0;
1541  istart = 0;
1542  first = 0;
1543  for (i = 0; i < n1; i++) {
1544  numaGetIValue(na1, i, &ival);
1545  if (ival >= prev) {
1546  prev = ival;
1547  continue;
1548  } else {
1549  first = prev;
1550  istart = i - 1;
1551  break;
1552  }
1553  }
1554  prev = 0;
1555  last = 0;
1556  iend = n1 - 1;
1557  for (i = n1 - 1; i >= 0; i--) {
1558  numaGetIValue(na1, i, &ival);
1559  if (ival >= prev) {
1560  prev = ival;
1561  continue;
1562  } else {
1563  last = prev;
1564  iend = i + 1;
1565  break;
1566  }
1567  }
1568  na2 = numaClipToInterval(na1, istart, iend);
1569  numaDestroy(&na1);
1570  n2 = numaGetCount(na2);
1571  delta = (parity == 0) ? last - first : first - last;
1572  denom = L_MAX(1.0, (l_float32)(L_MIN(first, last)));
1573  fract = (l_float32)delta / denom;
1574  if (dew->debug) {
1575  L_INFO("Slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1576  __func__, first, last, fract);
1577  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0092", NULL);
1578  lept_mv("/tmp/lept/dew/0092.png", "lept/dewmod", NULL, NULL);
1579  }
1580  if (fract < fractthresh) {
1581  L_INFO("Small slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1582  __func__, first, last, fract);
1583  numaDestroy(&na2);
1584  return 0;
1585  }
1586 
1587  /* Find the density far from the binding, and normalize to 1. */
1588  ne = n2 - n2 % 2;
1589  if (parity == 0)
1590  numaGetSumOnInterval(na2, 0, ne / 2 - 1, &sum);
1591  else /* parity == 1 */
1592  numaGetSumOnInterval(na2, ne / 2, ne - 1, &sum);
1593  denom = L_MAX(1.0, (l_float32)(ne / 2));
1594  aveval = sum / denom;
1595  na3 = numaMakeConstant(aveval, n2);
1596  numaArithOp(na2, na2, na3, L_ARITH_DIVIDE);
1597  numaDestroy(&na3);
1598  if (dew->debug) {
1599  L_INFO("Average background density: %5.1f\n", __func__, aveval);
1600  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0093", NULL);
1601  lept_mv("/tmp/lept/dew/0093.png", "lept/dewmod", NULL, NULL);
1602  }
1603 
1604  /* Fit the normalized density curve to a quartic */
1605  pta1 = numaConvertToPta1(na2);
1606  ptaWriteStream(stderr, pta1, 0);
1607 /* ptaGetQuadraticLSF(pta1, NULL, NULL, NULL, &na3); */
1608  ptaGetQuarticLSF(pta1, &ca, &cb, &cc, &cd, &ce, &na3);
1609  ptaGetArrays(pta1, &na4, NULL);
1610  if (dew->debug) {
1611  gplot = gplotSimpleXY1(na4, na3, GPLOT_LINES, GPLOT_PNG,
1612  "/tmp/lept/dew/0094", NULL);
1613  gplotDestroy(&gplot);
1614  lept_mv("/tmp/lept/dew/0094.png", "lept/dewmod", NULL, NULL);
1615  }
1616  ptaDestroy(&pta1);
1617 
1618  /* Integrate from the high point down to 1 (or v.v) to get the
1619  * disparity needed to make the density constant. */
1620  nasum = numaMakeConstant(0, w); /* area under the curve above 1.0 */
1621  if (parity == 0) {
1622  for (i = n2 - 1; i >= 0; i--) {
1623  numaGetFValue(na3, i, &fval);
1624  if (fval < 1.0) break;
1625  }
1626  numaGetIValue(na4, i + 1, &x0);
1627  numaGetIValue(na4, n2 - 1, &x1);
1628  numaSetParameters(nasum, x0, 1);
1629  sum = 0.0;
1630  for (x = x0; x < x1; x++) {
1631  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1632  sum += (y - 1.0);
1633  numaReplaceNumber(nasum, x, sum);
1634  }
1635  for (x = x1; x < w; x++)
1636  numaReplaceNumber(nasum, x, sum);
1637  } else { /* parity == 1 */
1638  for (i = 0; i < n2; i++) {
1639  numaGetFValue(na3, i, &fval);
1640  if (fval < 1.0) break;
1641  }
1642  numaGetIValue(na4, 0, &x0);
1643  numaGetIValue(na4, i - 1, &x1);
1644  numaSetParameters(nasum, x0, 1);
1645  sum = 0.0;
1646  for (x = x1; x >= x0; x--) {
1647  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1648  sum += (y - 1.0);
1649  numaReplaceNumber(nasum, x, sum);
1650  }
1651  for (x = x0; x >= 0; x--)
1652  numaReplaceNumber(nasum, x, sum);
1653  }
1654 
1655  /* Save the result in a fpix at the specified subsampling */
1656  nx = dew->nx;
1657  ny = dew->ny;
1658  fpix = fpixCreate(nx, ny);
1659  del = (l_float32)w / (l_float32)nx;
1660  for (i = 0; i < ny; i++) {
1661  for (j = 0; j < nx; j++) {
1662  x = del * j;
1663  numaGetFValue(nasum, x, &fval);
1664  fpixSetPixel(fpix, j, i, fval);
1665  }
1666  }
1667  dew->sampydispar = fpix;
1668  dew->ysuccess = 1;
1669 
1670  numaDestroy(&na2);
1671  numaDestroy(&na3);
1672  numaDestroy(&na4);
1673  numaDestroy(&nasum);
1674  return 0;
1675 }
1676 
1677 
1678 /*----------------------------------------------------------------------*
1679  * Build line disparity model *
1680  *----------------------------------------------------------------------*/
1709 l_ok
1711  l_int32 opensize,
1712  const char *debugfile)
1713 {
1714 char buf[64];
1715 l_int32 i, j, bx, by, ret, nlines;
1716 BOXA *boxa;
1717 PIX *pixs, *pixh, *pixv, *pix, *pix1, *pix2;
1718 PIXA *pixa1, *pixa2;
1719 PTA *pta;
1720 PTAA *ptaa1, *ptaa2;
1721 
1722  if (!dew)
1723  return ERROR_INT("dew not defined", __func__, 1);
1724  if (opensize < 3) {
1725  L_WARNING("opensize should be >= 3; setting to 8\n", __func__);
1726  opensize = 8; /* default */
1727  }
1728 
1729  dew->debug = (debugfile) ? 1 : 0;
1730  dew->vsuccess = dew->hsuccess = 0;
1731  pixs = dew->pixs;
1732  if (debugfile) {
1733  lept_rmdir("lept/dewline"); /* erase previous images */
1734  lept_mkdir("lept/dewline");
1735  lept_rmdir("lept/dewmod"); /* erase previous images */
1736  lept_mkdir("lept/dewmod");
1737  lept_mkdir("lept/dewarp");
1738  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
1739  pixWriteDebug("/tmp/lept/dewline/001.png", pixs, IFF_PNG);
1740  }
1741 
1742  /* Extract and solidify the horizontal and vertical lines. We use
1743  * the horizontal lines to derive the vertical disparity, and v.v.
1744  * Both disparities are computed using the vertical disparity
1745  * algorithm; the horizontal disparity is found from the
1746  * vertical lines by rotating them clockwise by 90 degrees.
1747  * On the first pass, we compute the horizontal disparity, from
1748  * the vertical lines, by rotating them by 90 degrees (so they
1749  * are horizontal) and computing the vertical disparity on them;
1750  * we rotate the resulting fpix array for the horizontal disparity
1751  * back by -90 degrees. On the second pass, we compute the vertical
1752  * disparity from the horizontal lines in the usual fashion. */
1753  snprintf(buf, sizeof(buf), "d1.3 + c%d.1 + o%d.1", opensize - 2, opensize);
1754  pixh = pixMorphSequence(pixs, buf, 0); /* horiz */
1755  snprintf(buf, sizeof(buf), "d3.1 + c1.%d + o1.%d", opensize - 2, opensize);
1756  pix1 = pixMorphSequence(pixs, buf, 0); /* vert */
1757  pixv = pixRotateOrth(pix1, 1); /* vert rotated to horizontal */
1758  pixa1 = pixaCreate(2);
1759  pixaAddPix(pixa1, pixv, L_INSERT); /* get horizontal disparity first */
1760  pixaAddPix(pixa1, pixh, L_INSERT);
1761  pixDestroy(&pix1);
1762 
1763  /*--------------------------------------------------------------*/
1764  /* Process twice: first for horiz disparity, then for vert */
1765  /*--------------------------------------------------------------*/
1766  for (i = 0; i < 2; i++) {
1767  pix = pixaGetPix(pixa1, i, L_CLONE);
1768  pixDisplay(pix, 0, 900);
1769  boxa = pixConnComp(pix, &pixa2, 8);
1770  nlines = boxaGetCount(boxa);
1771  boxaDestroy(&boxa);
1772  if (nlines < dew->minlines) {
1773  L_WARNING("only found %d lines\n", __func__, nlines);
1774  pixDestroy(&pix);
1775  pixaDestroy(&pixa1);
1776  continue;
1777  }
1778 
1779  /* Identify the pixels along the skeleton of each line */
1780  ptaa1 = ptaaCreate(nlines);
1781  for (j = 0; j < nlines; j++) {
1782  pixaGetBoxGeometry(pixa2, j, &bx, &by, NULL, NULL);
1783  pix1 = pixaGetPix(pixa2, j, L_CLONE);
1784  pta = dewarpGetMeanVerticals(pix1, bx, by);
1785  ptaaAddPta(ptaa1, pta, L_INSERT);
1786  pixDestroy(&pix1);
1787  }
1788  pixaDestroy(&pixa2);
1789  if (debugfile) {
1790  pix1 = pixConvertTo32(pix);
1791  pix2 = pixDisplayPtaa(pix1, ptaa1);
1792  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 2 + 2 * i);
1793  pixWriteDebug(buf, pix2, IFF_PNG);
1794  pixDestroy(&pix1);
1795  pixDestroy(&pix2);
1796  }
1797 
1798  /* Remove all lines that are not at least 0.75 times the length
1799  * of the longest line. */
1800  ptaa2 = dewarpRemoveShortLines(pix, ptaa1, 0.75, DEBUG_SHORT_LINES);
1801  if (debugfile) {
1802  pix1 = pixConvertTo32(pix);
1803  pix2 = pixDisplayPtaa(pix1, ptaa2);
1804  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 3 + 2 * i);
1805  pixWriteDebug(buf, pix2, IFF_PNG);
1806  pixDestroy(&pix1);
1807  pixDestroy(&pix2);
1808  }
1809  ptaaDestroy(&ptaa1);
1810  nlines = ptaaGetCount(ptaa2);
1811  if (nlines < dew->minlines) {
1812  pixDestroy(&pix);
1813  ptaaDestroy(&ptaa2);
1814  L_WARNING("%d lines: too few to build model\n", __func__, nlines);
1815  continue;
1816  }
1817 
1818  /* Get the sampled 'vertical' disparity from the textline
1819  * centers. The disparity array will push pixels vertically
1820  * so that each line is flat and centered at the y-position
1821  * of the mid-point. */
1822  ret = dewarpFindVertDisparity(dew, ptaa2, 1 - i);
1823 
1824  /* If i == 0, move the result to the horizontal disparity,
1825  * rotating it back by -90 degrees. */
1826  if (i == 0) { /* horizontal disparity, really */
1827  if (ret) {
1828  L_WARNING("horizontal disparity not built\n", __func__);
1829  } else {
1830  L_INFO("hsuccess = 1\n", __func__);
1831  dew->samphdispar = fpixRotateOrth(dew->sampvdispar, 3);
1832  fpixDestroy(&dew->sampvdispar);
1833  if (debugfile)
1834  lept_mv("/tmp/lept/dewarp/vert_disparity.pdf",
1835  "lept/dewarp", "horiz_disparity.pdf", NULL);
1836  }
1837  dew->hsuccess = dew->vsuccess;
1838  dew->vsuccess = 0;
1839  } else { /* i == 1 */
1840  if (ret)
1841  L_WARNING("vertical disparity not built\n", __func__);
1842  else
1843  L_INFO("vsuccess = 1\n", __func__);
1844  }
1845  ptaaDestroy(&ptaa2);
1846  pixDestroy(&pix);
1847  }
1848  pixaDestroy(&pixa1);
1849 
1850  /* Debug output */
1851  if (debugfile) {
1852  if (dew->vsuccess == 1) {
1853  dewarpPopulateFullRes(dew, NULL, 0, 0);
1854  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
1855  pixWriteDebug("/tmp/lept/dewline/006.png", pix1, IFF_PNG);
1856  pixDisplay(pix1, 1000, 0);
1857  pixDestroy(&pix1);
1858  }
1859  if (dew->hsuccess == 1) {
1860  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
1861  pixWriteDebug("/tmp/lept/dewline/007.png", pix1, IFF_PNG);
1862  pixDisplay(pix1, 1000, 0);
1863  pixDestroy(&pix1);
1864  }
1865  convertFilesToPdf("/tmp/lept/dewline", NULL, 135, 1.0, 0, 0,
1866  "Dewarp Build Line Model", debugfile);
1867  lept_stderr("pdf file: %s\n", debugfile);
1868  }
1869 
1870  return 0;
1871 }
1872 
1873 
1874 /*----------------------------------------------------------------------*
1875  * Query model status *
1876  *----------------------------------------------------------------------*/
1891 l_ok
1893  l_int32 pageno,
1894  l_int32 *pvsuccess,
1895  l_int32 *phsuccess)
1896 {
1897 L_DEWARP *dew;
1898 
1899  if (pvsuccess) *pvsuccess = 0;
1900  if (phsuccess) *phsuccess = 0;
1901  if (!dewa)
1902  return ERROR_INT("dewa not defined", __func__, 1);
1903 
1904  if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL)
1905  return ERROR_INT("dew not retrieved", __func__, 1);
1906  if (pvsuccess) *pvsuccess = dew->vsuccess;
1907  if (phsuccess) *phsuccess = dew->hsuccess;
1908  return 0;
1909 }
1910 
1911 
1912 /*----------------------------------------------------------------------*
1913  * Rendering helpers *
1914  *----------------------------------------------------------------------*/
1923 static l_int32
1925  NUMA *namidys,
1926  l_int32 linew)
1927 {
1928 l_int32 i, n, w, yval, rval, gval, bval;
1929 PIXCMAP *cmap;
1930 
1931  if (!pixs)
1932  return ERROR_INT("pixs not defined", __func__, 1);
1933  if (!namidys)
1934  return ERROR_INT("namidys not defined", __func__, 1);
1935 
1936  w = pixGetWidth(pixs);
1937  n = numaGetCount(namidys);
1938  cmap = pixcmapCreateRandom(8, 0, 0);
1939  for (i = 0; i < n; i++) {
1940  pixcmapGetColor(cmap, i % 256, &rval, &gval, &bval);
1941  numaGetIValue(namidys, i, &yval);
1942  pixRenderLineArb(pixs, 0, yval, w, yval, linew, rval, gval, bval);
1943  }
1944  pixcmapDestroy(&cmap);
1945  return 0;
1946 }
1947 
1948 
1958 static l_int32
1960  PTA *ptal,
1961  PTA *ptar,
1962  l_uint32 color)
1963 {
1964 PIX *pixcirc;
1965 PTA *ptalt, *ptart, *ptacirc;
1966 
1967  if (!pixs)
1968  return ERROR_INT("pixs not defined", __func__, 1);
1969  if (!ptal || !ptar)
1970  return ERROR_INT("ptal and ptar not both defined", __func__, 1);
1971 
1972  ptacirc = generatePtaFilledCircle(5);
1973  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
1974  ptalt = ptaTranspose(ptal);
1975  ptart = ptaTranspose(ptar);
1976 
1977  pixDisplayPtaPattern(pixs, pixs, ptalt, pixcirc, 5, 5, color);
1978  pixDisplayPtaPattern(pixs, pixs, ptart, pixcirc, 5, 5, color);
1979  ptaDestroy(&ptacirc);
1980  ptaDestroy(&ptalt);
1981  ptaDestroy(&ptart);
1982  pixDestroy(&pixcirc);
1983  return 0;
1984 }
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:273
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:519
l_int32 boxaGetCount(const BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:661
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:171
l_ok boxaContainedInBoxCount(BOXA *boxa, BOX *box, l_int32 *pcount)
boxaContainedInBoxCount()
Definition: boxfunc1.c:229
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:217
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:272
PIXCMAP * pixcmapCreateRandom(l_int32 depth, l_int32 hasblack, l_int32 haswhite)
pixcmapCreateRandom()
Definition: colormap.c:171
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:789
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:307
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:152
L_DEWARP * dewarpaGetDewarp(L_DEWARPA *dewa, l_int32 index)
dewarpaGetDewarp()
Definition: dewarp1.c:895
static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew)
pixRenderMidYs()
Definition: dewarp2.c:1924
static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1, PTA **pptal2, PTA **pptar2)
dewarpFilterLineEndPoints()
Definition: dewarp2.c:1116
l_ok dewarpFindHorizDisparity(L_DEWARP *dew, PTAA *ptaa)
dewarpFindHorizDisparity()
Definition: dewarp2.c:563
static l_int32 dewarpLinearLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, l_float32 *pmederr)
dewarpLinearLSF()
Definition: dewarp2.c:1345
static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar, l_uint32 color)
pixRenderHorizEndPoints()
Definition: dewarp2.c:1959
l_ok dewarpBuildPageModel(L_DEWARP *dew, const char *debugfile)
dewarpBuildPageModel()
Definition: dewarp2.c:156
static PTA * dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas)
dewarpRemoveBadEndPoints()
Definition: dewarp2.c:1201
l_ok dewarpBuildLineModel(L_DEWARP *dew, l_int32 opensize, const char *debugfile)
dewarpBuildLineModel()
Definition: dewarp2.c:1710
PTAA * dewarpRemoveShortLines(PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag)
dewarpRemoveShortLines()
Definition: dewarp2.c:958
static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal, PTA **pptar)
dewarpGetLineEndPoints()
Definition: dewarp2.c:1039
static PTA * dewarpGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y)
dewarpGetMeanVerticals()
Definition: dewarp2.c:914
static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pmederr)
dewarpQuadraticLSF()
Definition: dewarp2.c:1401
static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h, l_int32 *pntop, l_int32 *pnbot, l_int32 *pytop, l_int32 *pybot)
dewarpIsLineCoverageValid()
Definition: dewarp2.c:1277
l_ok dewarpaModelStatus(L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess)
dewarpaModelStatus()
Definition: dewarp2.c:1892
PTAA * dewarpGetTextlineCenters(PIX *pixs, l_int32 debugflag)
dewarpGetTextlineCenters()
Definition: dewarp2.c:808
l_ok dewarpFindVertDisparity(L_DEWARP *dew, PTAA *ptaa, l_int32 rotflag)
dewarpFindVertDisparity()
Definition: dewarp2.c:301
l_ok dewarpFindHorizSlopeDisparity(L_DEWARP *dew, PIX *pixb, l_float32 fractthresh, l_int32 parity)
dewarpFindHorizSlopeDisparity()
Definition: dewarp2.c:1482
l_ok dewarpPopulateFullRes(L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y)
dewarpPopulateFullRes()
Definition: dewarp3.c:773
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:527
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:280
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:152
FPIX * fpixRotateOrth(FPIX *fpixs, l_int32 quads)
fpixRotateOrth()
Definition: fpix2.c:1706
GPLOT * gplotSimpleXY1(NUMA *nax, NUMA *nay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title)
gplotSimpleXY1()
Definition: gplot.c:887
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:253
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:649
l_ok pixRenderLineArb(PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderLineArb()
Definition: graphics.c:1493
PTA * generatePtaFilledCircle(l_int32 radius)
generatePtaFilledCircle()
Definition: graphics.c:811
PIX * fpixRenderContours(FPIX *fpixs, l_float32 incr, l_float32 proxim)
fpixRenderContours()
Definition: graphics.c:2720
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:460
l_ok numaReplaceNumber(NUMA *na, l_int32 index, l_float32 val)
numaReplaceNumber()
Definition: numabasic.c:601
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:687
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:193
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:357
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:720
l_ok numaWriteDebug(const char *filename, NUMA *na)
numaWriteDebug()
Definition: numabasic.c:1129
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:910
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:850
NUMA * numaSort(NUMA *naout, NUMA *nain, l_int32 sortorder)
numaSort()
Definition: numafunc1.c:2567
l_ok numaGetMedianDevFromMedian(NUMA *na, l_float32 *pmed, l_float32 *pdev)
numaGetMedianDevFromMedian()
Definition: numafunc1.c:3396
l_ok numaGetMedian(NUMA *na, l_float32 *pval)
numaGetMedian()
Definition: numafunc1.c:3296
NUMA * numaGetSortIndex(NUMA *na, l_int32 sortorder)
numaGetSortIndex()
Definition: numafunc1.c:2664
l_ok numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:596
l_ok numaGetMin(NUMA *na, l_float32 *pminval, l_int32 *piminloc)
numaGetMin()
Definition: numafunc1.c:444
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:820
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1137
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:485
NUMA * numaArithOp(NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op)
numaArithOp()
Definition: numafunc1.c:174
NUMA * numaSortByIndex(NUMA *nas, NUMA *naindex)
numaSortByIndex()
Definition: numafunc1.c:2825
l_ok convertFilesToPdf(const char *dirname, const char *substr, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout)
convertFilesToPdf()
Definition: pdfio1.c:252
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:689
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1654
@ L_SELECT_IF_GT
Definition: pix.h:576
@ L_SELECT_IF_BOTH
Definition: pix.h:599
@ L_SELECT_HEIGHT
Definition: pix.h:594
@ L_SORT_BY_Y
Definition: pix.h:529
@ L_SORT_BY_X
Definition: pix.h:528
@ L_CLONE
Definition: pix.h:506
@ L_NOCOPY
Definition: pix.h:503
@ L_INSERT
Definition: pix.h:504
@ L_SORT_DECREASING
Definition: pix.h:523
@ L_SORT_INCREASING
Definition: pix.h:522
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:629
l_ok pixaGetBoxGeometry(PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
pixaGetBoxGeometry()
Definition: pixabasic.c:800
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:647
PIXA * pixaSelectBySize(PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixaSelectBySize()
Definition: pixafunc1.c:305
PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h)
pixaDisplay()
Definition: pixafunc2.c:191
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3246
l_ok ptaWriteStream(FILE *fp, PTA *pta, l_int32 type)
ptaWriteStream()
Definition: ptabasic.c:806
PTA * ptaCreateFromNuma(NUMA *nax, NUMA *nay)
ptaCreateFromNuma()
Definition: ptabasic.c:150
PTAA * ptaaCreate(l_int32 n)
ptaaCreate()
Definition: ptabasic.c:905
PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag)
ptaaGetPta()
Definition: ptabasic.c:1064
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:527
l_ok ptaWriteDebug(const char *filename, PTA *pta, l_int32 type)
ptaWriteDebug()
Definition: ptabasic.c:753
l_ok ptaaAddPta(PTAA *ptaa, PTA *pta, l_int32 copyflag)
ptaaAddPta()
Definition: ptabasic.c:963
l_int32 ptaaGetCount(PTAA *ptaa)
ptaaGetCount()
Definition: ptabasic.c:1046
l_ok ptaaGetPt(PTAA *ptaa, l_int32 ipta, l_int32 jpt, l_float32 *px, l_float32 *py)
ptaaGetPt()
Definition: ptabasic.c:1093
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:328
l_ok ptaaWriteDebug(const char *filename, PTAA *ptaa, l_int32 type)
ptaaWriteDebug()
Definition: ptabasic.c:1377
l_ok ptaGetArrays(PTA *pta, NUMA **pnax, NUMA **pnay)
ptaGetArrays()
Definition: ptabasic.c:584
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:499
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:480
void ptaaDestroy(PTAA **pptaa)
ptaaDestroy()
Definition: ptabasic.c:930
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:120
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:191
l_ok applyLinearFit(l_float32 a, l_float32 b, l_float32 x, l_float32 *py)
applyLinearFit()
Definition: ptafunc1.c:1704
l_ok ptaGetQuarticLSF(PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pd, l_float32 *pe, NUMA **pnafit)
ptaGetQuarticLSF()
Definition: ptafunc1.c:1407
PIX * pixDisplayPtaPattern(PIX *pixd, PIX *pixs, PTA *pta, PIX *pixp, l_int32 cx, l_int32 cy, l_uint32 color)
pixDisplayPtaPattern()
Definition: ptafunc1.c:2478
PTA * numaConvertToPta1(NUMA *na)
numaConvertToPta1()
Definition: ptafunc1.c:2238
l_ok applyQuadraticFit(l_float32 a, l_float32 b, l_float32 c, l_float32 x, l_float32 *py)
applyQuadraticFit()
Definition: ptafunc1.c:1726
PTA * ptaTranspose(PTA *ptas)
ptaTranspose()
Definition: ptafunc1.c:286
PIX * pixDisplayPtaa(PIX *pixs, PTAA *ptaa)
pixDisplayPtaa()
Definition: ptafunc1.c:2587
PTA * ptaSelectRange(PTA *ptas, l_int32 first, l_int32 last)
ptaSelectRange()
Definition: ptafunc1.c:378
l_ok ptaGetMinMax(PTA *pta, l_float32 *pxmin, l_float32 *pymin, l_float32 *pxmax, l_float32 *pymax)
ptaGetMinMax()
Definition: ptafunc1.c:887
l_ok applyQuarticFit(l_float32 a, l_float32 b, l_float32 c, l_float32 d, l_float32 e, l_float32 x, l_float32 *py)
applyQuarticFit()
Definition: ptafunc1.c:1773
l_ok ptaGetQuadraticLSF(PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, NUMA **pnafit)
ptaGetQuadraticLSF()
Definition: ptafunc1.c:1168
l_ok ptaGetLinearLSF(PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit)
ptaGetLinearLSF()
Definition: ptafunc1.c:1069
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:166
PIX * pixDisplayPtaaPattern(PIX *pixd, PIX *pixs, PTAA *ptaa, PIX *pixp, l_int32 cx, l_int32 cy)
pixDisplayPtaaPattern()
Definition: ptafunc1.c:2413
l_ok ptaGetRange(PTA *pta, l_float32 *pminx, l_float32 *pmaxx, l_float32 *pminy, l_float32 *pmaxy)
ptaGetRange()
Definition: ptafunc1.c:473
PIX * pixGenerateFromPta(PTA *pta, l_int32 w, l_int32 h)
pixGenerateFromPta()
Definition: ptafunc1.c:1962
PIX * pixDisplayPta(PIX *pixd, PIX *pixs, PTA *pta)
pixDisplayPta()
Definition: ptafunc1.c:2349
PTA * ptaSort(PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex)
ptaSort()
Definition: ptafunc2.c:97
PTAA * ptaaSortByIndex(PTAA *ptaas, NUMA *naindex)
ptaaSortByIndex()
Definition: ptafunc2.c:220
l_ok ptaGetRankValue(PTA *pta, l_float32 fract, PTA *ptasort, l_int32 sorttype, l_float32 *pval)
ptaGetRankValue()
Definition: ptafunc2.c:257
PIX * pixRotateOrth(PIX *pixs, l_int32 quads)
pixRotateOrth()
Definition: rotateorth.c:75
PIX * pixSeedfillBinary(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillBinary()
Definition: seedfill.c:247
Definition: gplot.h:77
l_int32 minlines
Definition: dewarp.h:168
struct FPix * fullvdispar
Definition: dewarp.h:158
l_int32 mincurv
Definition: dewarp.h:170
l_int32 pageno
Definition: dewarp.h:165
l_int32 vsuccess
Definition: dewarp.h:180
struct L_Dewarpa * dewa
Definition: dewarp.h:153
l_int32 sampling
Definition: dewarp.h:166
l_int32 hsuccess
Definition: dewarp.h:181
l_int32 leftcurv
Definition: dewarp.h:174
l_int32 vvalid
Definition: dewarp.h:183
l_int32 ysuccess
Definition: dewarp.h:182
struct Pix * pixs
Definition: dewarp.h:154
l_int32 nx
Definition: dewarp.h:176
l_int32 nlines
Definition: dewarp.h:169
l_int32 hvalid
Definition: dewarp.h:184
l_int32 maxcurv
Definition: dewarp.h:171
struct FPix * fullhdispar
Definition: dewarp.h:159
struct FPix * sampydispar
Definition: dewarp.h:157
struct FPix * sampvdispar
Definition: dewarp.h:155
struct Numa * nacurves
Definition: dewarp.h:162
l_int32 ny
Definition: dewarp.h:177
struct Numa * namidys
Definition: dewarp.h:161
struct FPix * samphdispar
Definition: dewarp.h:156
l_int32 rightcurv
Definition: dewarp.h:175
l_int32 leftslope
Definition: dewarp.h:172
l_int32 rightslope
Definition: dewarp.h:173
l_int32 debug
Definition: dewarp.h:187
l_int32 max_edgecurv
Definition: dewarp.h:135
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:690
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2213
l_int32 lept_mv(const char *srcfile, const char *newdir, const char *newtail, char **pnewpath)
lept_mv()
Definition: utils2.c:2482
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2138