Leptonica  1.82.0
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  PROCNAME("dewarpBuildPageModel");
165 
166  if (!dew)
167  return ERROR_INT("dew not defined", procName, 1);
168 
169  dew->debug = (debugfile) ? 1 : 0;
170  dew->vsuccess = dew->hsuccess = 0;
171  pixs = dew->pixs;
172  if (debugfile) {
173  lept_rmdir("lept/dewmod"); /* erase previous images */
174  lept_mkdir("lept/dewmod");
175  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
176  pixWriteDebug("/tmp/lept/dewmod/0010.png", pixs, IFF_PNG);
177  }
178 
179  /* Make initial estimate of centers of textlines */
180  ptaa1 = dewarpGetTextlineCenters(pixs, debugfile || DEBUG_TEXTLINE_CENTERS);
181  if (!ptaa1) {
182  L_WARNING("textline centers not found; model not built\n", procName);
183  return 1;
184  }
185  if (debugfile) {
186  pix1 = pixConvertTo32(pixs);
187  pta = generatePtaFilledCircle(1);
188  pix2 = pixGenerateFromPta(pta, 5, 5);
189  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa1, pix2, 2, 2);
190  pixWriteDebug("/tmp/lept/dewmod/0020.png", pix3, IFF_PNG);
191  pixDestroy(&pix1);
192  pixDestroy(&pix2);
193  pixDestroy(&pix3);
194  ptaDestroy(&pta);
195  }
196 
197  /* Remove all lines that are not at least 0.8 times the length
198  * of the longest line. */
199  ptaa2 = dewarpRemoveShortLines(pixs, ptaa1, 0.8,
200  debugfile || DEBUG_SHORT_LINES);
201  if (debugfile) {
202  pix1 = pixConvertTo32(pixs);
203  pta = generatePtaFilledCircle(1);
204  pix2 = pixGenerateFromPta(pta, 5, 5);
205  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa2, pix2, 2, 2);
206  pixWriteDebug("/tmp/lept/dewmod/0030.png", pix3, IFF_PNG);
207  pixDestroy(&pix1);
208  pixDestroy(&pix2);
209  pixDestroy(&pix3);
210  ptaDestroy(&pta);
211  }
212  ptaaDestroy(&ptaa1);
213 
214  /* Verify that there are sufficient "long" lines */
215  linecount = ptaaGetCount(ptaa2);
216  if (linecount < dew->minlines) {
217  ptaaDestroy(&ptaa2);
218  L_WARNING("linecount %d < min req'd number of lines (%d) for model\n",
219  procName, linecount, dew->minlines);
220  return 1;
221  }
222 
223  /* Verify that the lines have a reasonable coverage of the
224  * vertical extent of the page. */
225  if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs),
226  &ntop, &nbot, &ytop, &ybot) == FALSE) {
227  ptaaDestroy(&ptaa2);
228  L_WARNING("invalid line coverage: ntop = %d, nbot = %d;"
229  " spanning [%d ... %d] in height %d\n", procName,
230  ntop, nbot, ytop, ybot, pixGetHeight(pixs));
231  return 1;
232  }
233 
234  /* Get the sampled vertical disparity from the textline centers.
235  * The disparity array will push pixels vertically so that each
236  * textline is flat and centered at the y-position of the mid-point. */
237  if (dewarpFindVertDisparity(dew, ptaa2, 0) != 0) {
238  L_WARNING("vertical disparity not built\n", procName);
239  ptaaDestroy(&ptaa2);
240  return 1;
241  }
242 
243  /* Get the sampled horizontal disparity from the left and right
244  * edges of the text. The disparity array will expand the image
245  * linearly outward to align the text edges vertically.
246  * Do this even if useboth == 0; we still calculate it even
247  * if we don't plan to use it. */
248  if ((ret = dewarpFindHorizDisparity(dew, ptaa2)) == 0)
249  L_INFO("hsuccess = 1\n", procName);
250 
251  /* Debug output */
252  if (debugfile) {
253  dewarpPopulateFullRes(dew, NULL, 0, 0);
254  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
255  pixWriteDebug("/tmp/lept/dewmod/0060.png", pix1, IFF_PNG);
256  pixDisplay(pix1, 1000, 0);
257  pixDestroy(&pix1);
258  if (ret == 0) {
259  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
260  pixWriteDebug("/tmp/lept/dewmod/0070.png", pix1, IFF_PNG);
261  pixDisplay(pix1, 1000, 0);
262  pixDestroy(&pix1);
263  }
264  convertFilesToPdf("/tmp/lept/dewmod", NULL, 135, 1.0, 0, 0,
265  "Dewarp Build Model", debugfile);
266  lept_stderr("pdf file: %s\n", debugfile);
267  }
268 
269  ptaaDestroy(&ptaa2);
270  return 0;
271 }
272 
273 
302 l_ok
304  PTAA *ptaa,
305  l_int32 rotflag)
306 {
307 l_int32 i, j, nlines, npts, nx, ny, sampling;
308 l_float32 c0, c1, c2, x, y, midy, val, medval, meddev, minval, maxval;
309 l_float32 *famidys;
310 NUMA *nax, *nafit, *nacurve0, *nacurve1, *nacurves;
311 NUMA *namidy, *namidys, *namidysi;
312 PIX *pix1, *pix2, *pixcirc, *pixdb;
313 PTA *pta, *ptad, *ptacirc;
314 PTAA *ptaa0, *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaat;
315 FPIX *fpix;
316 
317  PROCNAME("dewarpFindVertDisparity");
318 
319  if (!dew)
320  return ERROR_INT("dew not defined", procName, 1);
321  dew->vsuccess = 0;
322  if (!ptaa)
323  return ERROR_INT("ptaa not defined", procName, 1);
324 
325  if (dew->debug) L_INFO("finding vertical disparity\n", procName);
326 
327  /* Do quadratic fit to smooth each line. A single quadratic
328  * over the entire width of the line appears to be sufficient.
329  * Quartics tend to overfit to noise. Each line is thus
330  * represented by three coefficients: y(x) = c2 * x^2 + c1 * x + c0.
331  * Using the coefficients, sample each fitted curve uniformly
332  * across the full width of the image. The result is in ptaa0. */
333  sampling = dew->sampling;
334  nx = (rotflag) ? dew->ny : dew->nx;
335  ny = (rotflag) ? dew->nx : dew->ny;
336  nlines = ptaaGetCount(ptaa);
337  dew->nlines = nlines;
338  ptaa0 = ptaaCreate(nlines);
339  nacurve0 = numaCreate(nlines); /* stores curvature coeff c2 */
340  pixdb = (rotflag) ? pixRotateOrth(dew->pixs, 1) : pixClone(dew->pixs);
341  for (i = 0; i < nlines; i++) { /* for each line */
342  pta = ptaaGetPta(ptaa, i, L_CLONE);
343  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
344  numaAddNumber(nacurve0, c2);
345  ptad = ptaCreate(nx);
346  for (j = 0; j < nx; j++) { /* uniformly sampled in x */
347  x = j * sampling;
348  applyQuadraticFit(c2, c1, c0, x, &y);
349  ptaAddPt(ptad, x, y);
350  }
351  ptaaAddPta(ptaa0, ptad, L_INSERT);
352  ptaDestroy(&pta);
353  }
354  if (dew->debug) {
355  lept_mkdir("lept/dewarp");
356  lept_mkdir("lept/dewdebug");
357  lept_mkdir("lept/dewmod");
358  ptaat = ptaaCreate(nlines);
359  for (i = 0; i < nlines; i++) {
360  pta = ptaaGetPta(ptaa, i, L_CLONE);
361  ptaGetArrays(pta, &nax, NULL);
362  ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit);
363  ptad = ptaCreateFromNuma(nax, nafit);
364  ptaaAddPta(ptaat, ptad, L_INSERT);
365  ptaDestroy(&pta);
366  numaDestroy(&nax);
367  numaDestroy(&nafit);
368  }
369  pix1 = pixConvertTo32(pixdb);
370  pta = generatePtaFilledCircle(1);
371  pixcirc = pixGenerateFromPta(pta, 5, 5);
372  pix2 = pixDisplayPtaaPattern(NULL, pix1, ptaat, pixcirc, 2, 2);
373  pixWriteDebug("/tmp/lept/dewmod/0041.png", pix2, IFF_PNG);
374  pixDestroy(&pix1);
375  pixDestroy(&pix2);
376  ptaDestroy(&pta);
377  pixDestroy(&pixcirc);
378  ptaaDestroy(&ptaat);
379  }
380 
381  /* Remove lines with outlier curvatures.
382  * Note that this is just looking for internal consistency in
383  * the line curvatures. It is not rejecting lines based on
384  * the magnitude of the curvature. That is done when constraints
385  * are applied for valid models. */
386  numaGetMedianDevFromMedian(nacurve0, &medval, &meddev);
387  L_INFO("\nPage %d\n", procName, dew->pageno);
388  L_INFO("Pass 1: Curvature: medval = %f, meddev = %f\n",
389  procName, medval, meddev);
390  ptaa1 = ptaaCreate(nlines);
391  nacurve1 = numaCreate(nlines);
392  for (i = 0; i < nlines; i++) { /* for each line */
393  numaGetFValue(nacurve0, i, &val);
394  if (L_ABS(val - medval) > 7.0 * meddev) /* TODO: reduce to ~ 3.0 */
395  continue;
396  pta = ptaaGetPta(ptaa0, i, L_CLONE);
397  ptaaAddPta(ptaa1, pta, L_INSERT);
398  numaAddNumber(nacurve1, val);
399  }
400  nlines = ptaaGetCount(ptaa1);
401  numaDestroy(&nacurve0);
402 
403  /* Save the min and max curvature (in micro-units) */
404  numaGetMin(nacurve1, &minval, NULL);
405  numaGetMax(nacurve1, &maxval, NULL);
406  dew->mincurv = lept_roundftoi(1000000. * minval);
407  dew->maxcurv = lept_roundftoi(1000000. * maxval);
408  L_INFO("Pass 2: Min/max curvature = (%d, %d)\n", procName,
409  dew->mincurv, dew->maxcurv);
410 
411  /* Find and save the y values at the mid-points in each curve.
412  * If the slope is zero anywhere, it will typically be here. */
413  namidy = numaCreate(nlines);
414  for (i = 0; i < nlines; i++) {
415  pta = ptaaGetPta(ptaa1, i, L_CLONE);
416  npts = ptaGetCount(pta);
417  ptaGetPt(pta, npts / 2, NULL, &midy);
418  numaAddNumber(namidy, midy);
419  ptaDestroy(&pta);
420  }
421 
422  /* Sort the lines in ptaa1c by their vertical position, going down */
423  namidysi = numaGetSortIndex(namidy, L_SORT_INCREASING);
424  namidys = numaSortByIndex(namidy, namidysi);
425  nacurves = numaSortByIndex(nacurve1, namidysi);
426  numaDestroy(&dew->namidys); /* in case previously made */
427  numaDestroy(&dew->nacurves);
428  dew->namidys = namidys;
429  dew->nacurves = nacurves;
430  ptaa2 = ptaaSortByIndex(ptaa1, namidysi);
431  numaDestroy(&namidy);
432  numaDestroy(&nacurve1);
433  numaDestroy(&namidysi);
434  if (dew->debug) {
435  numaWriteDebug("/tmp/lept/dewdebug/midys.na", namidys);
436  numaWriteDebug("/tmp/lept/dewdebug/curves.na", nacurves);
437  pix1 = pixConvertTo32(pixdb);
438  ptacirc = generatePtaFilledCircle(5);
439  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
440  srand(3);
441  pixDisplayPtaaPattern(pix1, pix1, ptaa2, pixcirc, 5, 5);
442  srand(3); /* use the same colors for text and reference lines */
443  pixRenderMidYs(pix1, namidys, 2);
444  pix2 = (rotflag) ? pixRotateOrth(pix1, 3) : pixClone(pix1);
445  pixWriteDebug("/tmp/lept/dewmod/0042.png", pix2, IFF_PNG);
446  pixDisplay(pix2, 0, 0);
447  ptaDestroy(&ptacirc);
448  pixDestroy(&pixcirc);
449  pixDestroy(&pix1);
450  pixDestroy(&pix2);
451  }
452  pixDestroy(&pixdb);
453 
454  /* Convert the sampled points in ptaa2 to a sampled disparity with
455  * with respect to the y value at the mid point in the curve.
456  * The disparity is the distance the point needs to move;
457  * plus is downward. */
458  ptaa3 = ptaaCreate(nlines);
459  for (i = 0; i < nlines; i++) {
460  pta = ptaaGetPta(ptaa2, i, L_CLONE);
461  numaGetFValue(namidys, i, &midy);
462  ptad = ptaCreate(nx);
463  for (j = 0; j < nx; j++) {
464  ptaGetPt(pta, j, &x, &y);
465  ptaAddPt(ptad, x, midy - y);
466  }
467  ptaaAddPta(ptaa3, ptad, L_INSERT);
468  ptaDestroy(&pta);
469  }
470  if (dew->debug) {
471  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa3.ptaa", ptaa3, 0);
472  }
473 
474  /* Generate ptaa4 by taking vertical 'columns' from ptaa3.
475  * We want to fit the vertical disparity on the column to the
476  * vertical position of the line, which we call 'y' here and
477  * obtain from namidys. So each pta in ptaa4 is the set of
478  * vertical disparities down a column of points. The columns
479  * in ptaa4 are equally spaced in x. */
480  ptaa4 = ptaaCreate(nx);
481  famidys = numaGetFArray(namidys, L_NOCOPY);
482  for (j = 0; j < nx; j++) {
483  pta = ptaCreate(nlines);
484  for (i = 0; i < nlines; i++) {
485  y = famidys[i];
486  ptaaGetPt(ptaa3, i, j, NULL, &val); /* disparity value */
487  ptaAddPt(pta, y, val);
488  }
489  ptaaAddPta(ptaa4, pta, L_INSERT);
490  }
491  if (dew->debug) {
492  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa4.ptaa", ptaa4, 0);
493  }
494 
495  /* Do quadratic fit vertically on each of the pixel columns
496  * in ptaa4, for the vertical displacement (which identifies the
497  * src pixel(s) for each dest pixel) as a function of y (the
498  * y value of the mid-points for each line). Then generate
499  * ptaa5 by sampling the fitted vertical displacement on a
500  * regular grid in the vertical direction. Each pta in ptaa5
501  * gives the vertical displacement for regularly sampled y values
502  * at a fixed x. */
503  ptaa5 = ptaaCreate(nx); /* uniformly sampled across full height of image */
504  for (j = 0; j < nx; j++) { /* for each column */
505  pta = ptaaGetPta(ptaa4, j, L_CLONE);
506  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
507  ptad = ptaCreate(ny);
508  for (i = 0; i < ny; i++) { /* uniformly sampled in y */
509  y = i * sampling;
510  applyQuadraticFit(c2, c1, c0, y, &val);
511  ptaAddPt(ptad, y, val);
512  }
513  ptaaAddPta(ptaa5, ptad, L_INSERT);
514  ptaDestroy(&pta);
515  }
516  if (dew->debug) {
517  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa5.ptaa", ptaa5, 0);
518  convertFilesToPdf("/tmp/lept/dewmod", "004", 135, 1.0, 0, 0,
519  "Dewarp Vert Disparity",
520  "/tmp/lept/dewarp/vert_disparity.pdf");
521  lept_stderr("pdf file: /tmp/lept/dewarp/vert_disparity.pdf\n");
522  }
523 
524  /* Save the result in a fpix at the specified subsampling */
525  fpix = fpixCreate(nx, ny);
526  for (i = 0; i < ny; i++) {
527  for (j = 0; j < nx; j++) {
528  ptaaGetPt(ptaa5, j, i, NULL, &val);
529  fpixSetPixel(fpix, j, i, val);
530  }
531  }
532  dew->sampvdispar = fpix;
533  dew->vsuccess = 1;
534 
535  ptaaDestroy(&ptaa0);
536  ptaaDestroy(&ptaa1);
537  ptaaDestroy(&ptaa2);
538  ptaaDestroy(&ptaa3);
539  ptaaDestroy(&ptaa4);
540  ptaaDestroy(&ptaa5);
541  return 0;
542 }
543 
544 
566 l_ok
568  PTAA *ptaa)
569 {
570 l_int32 i, j, h, nx, ny, sampling, ret, linear_edgefit;
571 l_float32 c0, c1, cl0, cl1, cl2, cr0, cr1, cr2;
572 l_float32 x, y, refl, refr;
573 l_float32 val, mederr;
574 NUMA *nald, *nard;
575 PIX *pix1;
576 PTA *ptal1, *ptar1; /* left/right end points of lines; initial */
577 PTA *ptal2, *ptar2; /* left/right end points; after filtering */
578 PTA *ptal3, *ptar3; /* left and right block, fitted, uniform spacing */
579 PTA *pta, *ptat, *pta1, *pta2;
580 PTAA *ptaah;
581 FPIX *fpix;
582 
583  PROCNAME("dewarpFindHorizDisparity");
584 
585  if (!dew)
586  return ERROR_INT("dew not defined", procName, 1);
587  dew->hsuccess = 0;
588  if (!ptaa)
589  return ERROR_INT("ptaa not defined", procName, 1);
590 
591  if (dew->debug) L_INFO("finding horizontal disparity\n", procName);
592 
593  /* Get the endpoints of the lines, and sort from top to bottom */
594  h = pixGetHeight(dew->pixs);
595  ret = dewarpGetLineEndPoints(h, ptaa, &ptal1, &ptar1);
596  if (ret) {
597  L_INFO("Horiz disparity not built\n", procName);
598  return 1;
599  }
600  if (dew->debug) {
601  lept_mkdir("lept/dewdebug");
602  lept_mkdir("lept/dewarp");
603  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left1.pta", ptal1, 1);
604  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right1.pta", ptar1, 1);
605  }
606 
607  /* Filter the points by x-location to prevent 2-column images
608  * from getting confused about left and right endpoints. We
609  * require valid left points to not be farther than
610  * 0.20 * (remaining distance to the right edge of the image)
611  * to the right of the leftmost endpoint, and similarly for
612  * the right endpoints. (Note: x and y are reversed in the pta.)
613  * Also require end points to be near the medians in the
614  * upper and lower halves. */
615  ret = dewarpFilterLineEndPoints(dew, ptal1, ptar1, &ptal2, &ptar2);
616  ptaDestroy(&ptal1);
617  ptaDestroy(&ptar1);
618  if (ret) {
619  L_INFO("Not enough filtered end points\n", procName);
620  return 1;
621  }
622 
623  /* Do either a linear or a quadratic fit to the left and right
624  * endpoints of the longest lines. It is not necessary to use
625  * the noisy LSF fit function, because we've removed outlier
626  * end points by selecting the long lines.
627  * For the linear fit, each line is represented by 2 coefficients:
628  * x(y) = c1 * y + c0.
629  * For the quadratic fit, each line is represented by 3 coefficients:
630  * x(y) = c2 * y^2 + c1 * y + c0.
631  * Then using the coefficients, sample each fitted curve uniformly
632  * along the full height of the image. */
633  sampling = dew->sampling;
634  nx = dew->nx;
635  ny = dew->ny;
636  linear_edgefit = (dew->dewa->max_edgecurv == 0) ? TRUE : FALSE;
637 
638  if (linear_edgefit) {
639  /* Fit the left side, using linear LSF on the set of long lines. */
640  dewarpLinearLSF(ptal2, &cl1, &cl0, &mederr);
641  dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */
642  dew->leftcurv = 0; /* micro-units */
643  L_INFO("Left linear LSF median error = %5.2f\n", procName, mederr);
644  L_INFO("Left edge slope = %d\n", procName, dew->leftslope);
645  ptal3 = ptaCreate(ny);
646  for (i = 0; i < ny; i++) { /* uniformly sample in y */
647  y = i * sampling;
648  applyLinearFit(cl1, cl0, y, &x);
649  ptaAddPt(ptal3, x, y);
650  }
651 
652  /* Do a linear LSF on the right side. */
653  dewarpLinearLSF(ptar2, &cr1, &cr0, &mederr);
654  dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */
655  dew->rightcurv = 0; /* micro-units */
656  L_INFO("Right linear LSF median error = %5.2f\n", procName, mederr);
657  L_INFO("Right edge slope = %d\n", procName, dew->rightslope);
658  ptar3 = ptaCreate(ny);
659  for (i = 0; i < ny; i++) { /* uniformly sample in y */
660  y = i * sampling;
661  applyLinearFit(cr1, cr0, y, &x);
662  ptaAddPt(ptar3, x, y);
663  }
664  } else { /* quadratic edge fit */
665  /* Fit the left side, using quadratic LSF on the long lines. */
666  dewarpQuadraticLSF(ptal2, &cl2, &cl1, &cl0, &mederr);
667  dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */
668  dew->leftcurv = lept_roundftoi(1000000. * cl2); /* micro-units */
669  L_INFO("Left quad LSF median error = %5.2f\n", procName, mederr);
670  L_INFO("Left edge slope = %d\n", procName, dew->leftslope);
671  L_INFO("Left edge curvature = %d\n", procName, dew->leftcurv);
672  ptal3 = ptaCreate(ny);
673  for (i = 0; i < ny; i++) { /* uniformly sample in y */
674  y = i * sampling;
675  applyQuadraticFit(cl2, cl1, cl0, y, &x);
676  ptaAddPt(ptal3, x, y);
677  }
678 
679  /* Do a quadratic LSF on the right side. */
680  dewarpQuadraticLSF(ptar2, &cr2, &cr1, &cr0, &mederr);
681  dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */
682  dew->rightcurv = lept_roundftoi(1000000. * cr2); /* micro-units */
683  L_INFO("Right quad LSF median error = %5.2f\n", procName, mederr);
684  L_INFO("Right edge slope = %d\n", procName, dew->rightslope);
685  L_INFO("Right edge curvature = %d\n", procName, dew->rightcurv);
686  ptar3 = ptaCreate(ny);
687  for (i = 0; i < ny; i++) { /* uniformly sample in y */
688  y = i * sampling;
689  applyQuadraticFit(cr2, cr1, cr0, y, &x);
690  ptaAddPt(ptar3, x, y);
691  }
692  }
693 
694  if (dew->debug) {
695  PTA *ptalft, *ptarft;
696  h = pixGetHeight(dew->pixs);
697  pta1 = ptaCreate(h);
698  pta2 = ptaCreate(h);
699  if (linear_edgefit) {
700  for (i = 0; i < h; i++) {
701  applyLinearFit(cl1, cl0, i, &x);
702  ptaAddPt(pta1, x, i);
703  applyLinearFit(cr1, cr0, i, &x);
704  ptaAddPt(pta2, x, i);
705  }
706  } else { /* quadratic edge fit */
707  for (i = 0; i < h; i++) {
708  applyQuadraticFit(cl2, cl1, cl0, i, &x);
709  ptaAddPt(pta1, x, i);
710  applyQuadraticFit(cr2, cr1, cr0, i, &x);
711  ptaAddPt(pta2, x, i);
712  }
713  }
714  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
715  pixDisplayPta(pix1, pix1, pta2);
716  pixRenderHorizEndPoints(pix1, ptal2, ptar2, 0xff000000);
717  pixDisplay(pix1, 600, 800);
718  pixWriteDebug("/tmp/lept/dewmod/0051.png", pix1, IFF_PNG);
719  pixDestroy(&pix1);
720 
721  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
722  pixDisplayPta(pix1, pix1, pta2);
723  ptalft = ptaTranspose(ptal3);
724  ptarft = ptaTranspose(ptar3);
725  pixRenderHorizEndPoints(pix1, ptalft, ptarft, 0x0000ff00);
726  pixDisplay(pix1, 800, 800);
727  pixWriteDebug("/tmp/lept/dewmod/0052.png", pix1, IFF_PNG);
728  convertFilesToPdf("/tmp/lept/dewmod", "005", 135, 1.0, 0, 0,
729  "Dewarp Horiz Disparity",
730  "/tmp/lept/dewarp/horiz_disparity.pdf");
731  lept_stderr("pdf file: /tmp/lept/dewarp/horiz_disparity.pdf\n");
732  pixDestroy(&pix1);
733  ptaDestroy(&pta1);
734  ptaDestroy(&pta2);
735  ptaDestroy(&ptalft);
736  ptaDestroy(&ptarft);
737  }
738 
739  /* Find the x value at the midpoints (in y) of the two vertical lines,
740  * ptal3 and ptar3. These are the reference values for each of the
741  * lines. Then use the difference between the these midpoint
742  * values and the actual x coordinates of the lines to represent
743  * the horizontal disparity (nald, nard) on the vertical lines
744  * for the sampled y values. */
745  ptaGetPt(ptal3, ny / 2, &refl, NULL);
746  ptaGetPt(ptar3, ny / 2, &refr, NULL);
747  nald = numaCreate(ny);
748  nard = numaCreate(ny);
749  for (i = 0; i < ny; i++) {
750  ptaGetPt(ptal3, i, &x, NULL);
751  numaAddNumber(nald, refl - x);
752  ptaGetPt(ptar3, i, &x, NULL);
753  numaAddNumber(nard, refr - x);
754  }
755 
756  /* Now for each pair of sampled values of the two lines (at the
757  * same value of y), do a linear interpolation to generate
758  * the horizontal disparity on all sampled points between them. */
759  ptaah = ptaaCreate(ny);
760  for (i = 0; i < ny; i++) {
761  pta = ptaCreate(2);
762  numaGetFValue(nald, i, &val);
763  ptaAddPt(pta, refl, val);
764  numaGetFValue(nard, i, &val);
765  ptaAddPt(pta, refr, val);
766  ptaGetLinearLSF(pta, &c1, &c0, NULL); /* horiz disparity along line */
767  ptat = ptaCreate(nx);
768  for (j = 0; j < nx; j++) {
769  x = j * sampling;
770  applyLinearFit(c1, c0, x, &val);
771  ptaAddPt(ptat, x, val);
772  }
773  ptaaAddPta(ptaah, ptat, L_INSERT);
774  ptaDestroy(&pta);
775  }
776  numaDestroy(&nald);
777  numaDestroy(&nard);
778 
779  /* Save the result in a fpix at the specified subsampling */
780  fpix = fpixCreate(nx, ny);
781  for (i = 0; i < ny; i++) {
782  for (j = 0; j < nx; j++) {
783  ptaaGetPt(ptaah, i, j, NULL, &val);
784  fpixSetPixel(fpix, j, i, val);
785  }
786  }
787  dew->samphdispar = fpix;
788  dew->hsuccess = 1;
789  ptaDestroy(&ptal2);
790  ptaDestroy(&ptar2);
791  ptaDestroy(&ptal3);
792  ptaDestroy(&ptar3);
793  ptaaDestroy(&ptaah);
794  return 0;
795 }
796 
797 
813 PTAA *
815  l_int32 debugflag)
816 {
817 char buf[64];
818 l_int32 i, w, h, bx, by, nsegs, csize1, csize2;
819 BOXA *boxa;
820 PIX *pix1, *pix2;
821 PIXA *pixa1, *pixa2;
822 PTA *pta;
823 PTAA *ptaa;
824 
825  PROCNAME("dewarpGetTextlineCenters");
826 
827  if (!pixs || pixGetDepth(pixs) != 1)
828  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
829  pixGetDimensions(pixs, &w, &h, NULL);
830 
831  if (debugflag) L_INFO("finding text line centers\n", procName);
832 
833  /* Filter to solidify the text lines within the x-height region,
834  * and to remove most of the ascenders and descenders.
835  * We start with a small vertical opening to remove noise beyond
836  * the line that can cause an error in the line end points.
837  * The small closing (csize1) is used to bridge the gaps between
838  * letters. The large closing (csize2) bridges the gaps between
839  * words; using 1/30 of the page width usually suffices. */
840  csize1 = L_MAX(15, w / 80);
841  csize2 = L_MAX(40, w / 30);
842  snprintf(buf, sizeof(buf), "o1.3 + c%d.1 + o%d.1 + c%d.1",
843  csize1, csize1, csize2);
844  pix1 = pixMorphSequence(pixs, buf, 0);
845 
846  /* Remove the components (e.g., embedded images) that have
847  * long vertical runs (>= 50 pixels). You can't use bounding
848  * boxes because connected component b.b. of lines can be quite
849  * tall due to slope and curvature. */
850  pix2 = pixMorphSequence(pix1, "e1.50", 0); /* seed */
851  pixSeedfillBinary(pix2, pix2, pix1, 8); /* tall components */
852  pixXor(pix2, pix2, pix1); /* remove tall */
853 
854  if (debugflag) {
855  lept_mkdir("lept/dewmod");
856  pixWriteDebug("/tmp/lept/dewmod/0011.tif", pix1, IFF_TIFF_G4);
857  pixDisplayWithTitle(pix1, 0, 600, "pix1", 1);
858  pixWriteDebug("/tmp/lept/dewmod/0012.tif", pix2, IFF_TIFF_G4);
859  pixDisplayWithTitle(pix2, 0, 800, "pix2", 1);
860  }
861  pixDestroy(&pix1);
862 
863  /* Get the 8-connected components ... */
864  boxa = pixConnComp(pix2, &pixa1, 8);
865  pixDestroy(&pix2);
866  boxaDestroy(&boxa);
867  if (pixaGetCount(pixa1) == 0) {
868  pixaDestroy(&pixa1);
869  return NULL;
870  }
871 
872  /* ... and remove the short width and very short height c.c */
873  pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH,
874  L_SELECT_IF_GT, NULL);
875  if ((nsegs = pixaGetCount(pixa2)) == 0) {
876  pixaDestroy(&pixa1);
877  pixaDestroy(&pixa2);
878  return NULL;
879  }
880  if (debugflag) {
881  pix2 = pixaDisplay(pixa2, w, h);
882  pixWriteDebug("/tmp/lept/dewmod/0013.tif", pix2, IFF_TIFF_G4);
883  pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1);
884  pixDestroy(&pix2);
885  }
886 
887  /* For each c.c., get the weighted center of each vertical column.
888  * The result is a set of points going approximately through
889  * the center of the x-height part of the text line. */
890  ptaa = ptaaCreate(nsegs);
891  for (i = 0; i < nsegs; i++) {
892  pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL);
893  pix2 = pixaGetPix(pixa2, i, L_CLONE);
894  pta = dewarpGetMeanVerticals(pix2, bx, by);
895  ptaaAddPta(ptaa, pta, L_INSERT);
896  pixDestroy(&pix2);
897  }
898  if (debugflag) {
899  pix1 = pixCreateTemplate(pixs);
900  pix2 = pixDisplayPtaa(pix1, ptaa);
901  pixWriteDebug("/tmp/lept/dewmod/0014.tif", pix2, IFF_PNG);
902  pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1);
903  pixDestroy(&pix1);
904  pixDestroy(&pix2);
905  }
906 
907  pixaDestroy(&pixa1);
908  pixaDestroy(&pixa2);
909  return ptaa;
910 }
911 
912 
921 static PTA *
923  l_int32 x,
924  l_int32 y)
925 {
926 l_int32 w, h, i, j, wpl, sum, count;
927 l_uint32 *line, *data;
928 PTA *pta;
929 
930  PROCNAME("pixGetMeanVerticals");
931 
932  if (!pixs || pixGetDepth(pixs) != 1)
933  return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
934 
935  pixGetDimensions(pixs, &w, &h, NULL);
936  pta = ptaCreate(w);
937  data = pixGetData(pixs);
938  wpl = pixGetWpl(pixs);
939  for (j = 0; j < w; j++) {
940  line = data;
941  sum = count = 0;
942  for (i = 0; i < h; i++) {
943  if (GET_DATA_BIT(line, j) == 1) {
944  sum += i;
945  count += 1;
946  }
947  line += wpl;
948  }
949  if (count == 0) continue;
950  ptaAddPt(pta, x + j, y + (sum / count));
951  }
952 
953  return pta;
954 }
955 
956 
967 PTAA *
969  PTAA *ptaas,
970  l_float32 fract,
971  l_int32 debugflag)
972 {
973 l_int32 w, n, i, index, maxlen, len;
974 l_float32 minx, maxx;
975 NUMA *na, *naindex;
976 PIX *pix1, *pix2;
977 PTA *pta;
978 PTAA *ptaad;
979 
980  PROCNAME("dewarpRemoveShortLines");
981 
982  if (!pixs || pixGetDepth(pixs) != 1)
983  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
984  if (!ptaas)
985  return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL);
986 
987  pixGetDimensions(pixs, &w, NULL, NULL);
988  n = ptaaGetCount(ptaas);
989  ptaad = ptaaCreate(n);
990  na = numaCreate(n);
991  for (i = 0; i < n; i++) {
992  pta = ptaaGetPta(ptaas, i, L_CLONE);
993  ptaGetRange(pta, &minx, &maxx, NULL, NULL);
994  numaAddNumber(na, maxx - minx + 1);
995  ptaDestroy(&pta);
996  }
997 
998  /* Sort by length and find all that are long enough */
999  naindex = numaGetSortIndex(na, L_SORT_DECREASING);
1000  numaGetIValue(naindex, 0, &index);
1001  numaGetIValue(na, index, &maxlen);
1002  if (maxlen < 0.5 * w)
1003  L_WARNING("lines are relatively short\n", procName);
1004  pta = ptaaGetPta(ptaas, index, L_CLONE);
1005  ptaaAddPta(ptaad, pta, L_INSERT);
1006  for (i = 1; i < n; i++) {
1007  numaGetIValue(naindex, i, &index);
1008  numaGetIValue(na, index, &len);
1009  if (len < fract * maxlen) break;
1010  pta = ptaaGetPta(ptaas, index, L_CLONE);
1011  ptaaAddPta(ptaad, pta, L_INSERT);
1012  }
1013 
1014  if (debugflag) {
1015  pix1 = pixCopy(NULL, pixs);
1016  pix2 = pixDisplayPtaa(pix1, ptaad);
1017  pixDisplayWithTitle(pix2, 0, 200, "pix4", 1);
1018  pixDestroy(&pix1);
1019  pixDestroy(&pix2);
1020  }
1021 
1022  numaDestroy(&na);
1023  numaDestroy(&naindex);
1024  return ptaad;
1025 }
1026 
1027 
1050 static l_int32
1052  PTAA *ptaa,
1053  PTA **pptal,
1054  PTA **pptar)
1055 {
1056 l_int32 i, n, npt, x, y;
1057 l_float32 miny, maxy, ratio;
1058 PTA *pta, *ptal1, *ptar1;
1059 
1060  PROCNAME("dewarpGetLineEndPoints");
1061 
1062  if (!pptal || !pptar)
1063  return ERROR_INT("&ptal and &ptar not both defined", procName, 1);
1064  *pptal = *pptar = NULL;
1065  if (!ptaa)
1066  return ERROR_INT("ptaa undefined", procName, 1);
1067 
1068  /* Are there at least 10 lines? */
1069  n = ptaaGetCount(ptaa);
1070  if (n < MinLinesForHoriz1) {
1071  L_INFO("only %d lines; too few\n", procName, n);
1072  return 1;
1073  }
1074 
1075  /* Extract the line end points, and transpose x and y values */
1076  ptal1 = ptaCreate(n);
1077  ptar1 = ptaCreate(n);
1078  for (i = 0; i < n; i++) {
1079  pta = ptaaGetPta(ptaa, i, L_CLONE);
1080  ptaGetIPt(pta, 0, &x, &y);
1081  ptaAddPt(ptal1, y, x); /* transpose */
1082  npt = ptaGetCount(pta);
1083  ptaGetIPt(pta, npt - 1, &x, &y);
1084  ptaAddPt(ptar1, y, x); /* transpose */
1085  ptaDestroy(&pta);
1086  }
1087 
1088  /* Use the min and max of the y value on the left side. */
1089  ptaGetRange(ptal1, &miny, &maxy, NULL, NULL);
1090  ratio = (maxy - miny) / (l_float32)h;
1091  if (ratio < MinRatioLinesToHeight) {
1092  L_INFO("ratio lines to height, %f, too small\n", procName, ratio);
1093  ptaDestroy(&ptal1);
1094  ptaDestroy(&ptar1);
1095  return 1;
1096  }
1097 
1098  /* Sort from top to bottom */
1099  *pptal = ptaSort(ptal1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1100  *pptar = ptaSort(ptar1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1101  ptaDestroy(&ptal1);
1102  ptaDestroy(&ptar1);
1103  return 0;
1104 }
1105 
1106 
1129 static l_int32
1131  PTA *ptal,
1132  PTA *ptar,
1133  PTA **pptalf,
1134  PTA **pptarf)
1135 {
1136 l_int32 w, i, n;
1137 l_float32 ymin, ymax, xvall, xvalr, yvall, yvalr;
1138 PTA *ptal1, *ptar1, *ptal2, *ptar2;
1139 
1140  PROCNAME("dewarpFilterLineEndPoints");
1141  if (!ptal || !ptar)
1142  return ERROR_INT("ptal or ptar not defined", procName, 1);
1143  *pptalf = *pptarf = NULL;
1144 
1145  /* First filter for lines near left and right margins */
1146  w = pixGetWidth(dew->pixs);
1147  ptaGetMinMax(ptal, NULL, &ymin, NULL, NULL);
1148  ptaGetMinMax(ptar, NULL, NULL, NULL, &ymax);
1149  n = ptaGetCount(ptal); /* ptar is the same size; at least 10 */
1150  ptal1 = ptaCreate(n);
1151  ptar1 = ptaCreate(n);
1152  for (i = 0; i < n; i++) {
1153  ptaGetPt(ptal, i, &xvall, &yvall);
1154  ptaGetPt(ptar, i, &xvalr, &yvalr);
1155  if (yvall < ymin + 0.20 * (w - ymin) &&
1156  yvalr > 0.80 * ymax) {
1157  ptaAddPt(ptal1, xvall, yvall);
1158  ptaAddPt(ptar1, xvalr, yvalr);
1159  }
1160  }
1161  if (dew->debug) {
1162  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left2.pta", ptal1, 1);
1163  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right2.pta", ptar1, 1);
1164  }
1165 
1166  n = L_MIN(ptaGetCount(ptal1), ptaGetCount(ptar1));
1167  if (n < MinLinesForHoriz1 - 2) {
1168  ptaDestroy(&ptal1);
1169  ptaDestroy(&ptar1);
1170  L_INFO("First filter: only %d endpoints; needed 8\n", procName, n);
1171  return 1;
1172  }
1173 
1174  /* Remove outlier points */
1175  ptal2 = dewarpRemoveBadEndPoints(w, ptal1);
1176  ptar2 = dewarpRemoveBadEndPoints(w, ptar1);
1177  ptaDestroy(&ptal1);
1178  ptaDestroy(&ptar1);
1179  if (!ptal2 || !ptar2) {
1180  ptaDestroy(&ptal2);
1181  ptaDestroy(&ptar2);
1182  L_INFO("Second filter: too few endpoints left after outliers removed\n",
1183  procName);
1184  return 1;
1185  }
1186  if (dew->debug) {
1187  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left3.pta", ptal2, 1);
1188  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right3.pta", ptar2, 1);
1189  }
1190 
1191  *pptalf = ptal2;
1192  *pptarf = ptar2;
1193  return 0;
1194 }
1195 
1196 
1215 static PTA *
1217  PTA *ptas)
1218 {
1219 l_int32 i, n, nu, nd;
1220 l_float32 rval, xval, yval, delta;
1221 PTA *ptau1, *ptau2, *ptad1, *ptad2;
1222 
1223  PROCNAME("dewarpRemoveBadEndPoints");
1224 
1225  if (!ptas)
1226  return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
1227 
1228  delta = AllowedWidthFract * w;
1229  n = ptaGetCount(ptas); /* will be at least 8 */
1230 
1231  /* Check the upper half */
1232  ptau1 = ptaSelectRange(ptas, 0, n / 2);
1233  ptaGetRankValue(ptau1, 0.5, NULL, L_SORT_BY_Y, &rval);
1234  nu = ptaGetCount(ptau1);
1235  ptau2 = ptaCreate(nu);
1236  for (i = 0; i < nu; i++) {
1237  ptaGetPt(ptau1, i, &xval, &yval); /* transposed */
1238  if (L_ABS(rval - yval) <= delta)
1239  ptaAddPt(ptau2, xval, yval);
1240  }
1241  ptaDestroy(&ptau1);
1242  if (ptaGetCount(ptau2) < MinLinesForHoriz2) {
1243  ptaDestroy(&ptau2);
1244  L_INFO("Second filter: upper set is too small after outliers removed\n",
1245  procName);
1246  return NULL;
1247  }
1248 
1249  /* Check the lower half */
1250  ptad1 = ptaSelectRange(ptas, n / 2 + 1, -1);
1251  ptaGetRankValue(ptad1, 0.5, NULL, L_SORT_BY_Y, &rval);
1252  nd = ptaGetCount(ptad1);
1253  ptad2 = ptaCreate(nd);
1254  for (i = 0; i < nd; i++) {
1255  ptaGetPt(ptad1, i, &xval, &yval); /* transposed */
1256  if (L_ABS(rval - yval) <= delta)
1257  ptaAddPt(ptad2, xval, yval);
1258  }
1259  ptaDestroy(&ptad1);
1260  if (ptaGetCount(ptad2) < MinLinesForHoriz2) {
1261  ptaDestroy(&ptau2);
1262  ptaDestroy(&ptad2);
1263  L_INFO("Second filter: lower set is too small after outliers removed\n",
1264  procName);
1265  return NULL;
1266  }
1267 
1268  ptaJoin(ptau2, ptad2, 0, -1);
1269  ptaDestroy(&ptad2);
1270  return ptau2;
1271 }
1272 
1273 
1293 static l_int32
1295  l_int32 h,
1296  l_int32 *pntop,
1297  l_int32 *pnbot,
1298  l_int32 *pytop,
1299  l_int32 *pybot)
1300 {
1301 l_int32 i, n, iy, both_halves, ntop, nbot, ytop, ybot, nmin;
1302 l_float32 y, fraction;
1303 NUMA *na;
1304 
1305  PROCNAME("dewarpIsLineCoverageValid");
1306 
1307  if (!ptaa)
1308  return ERROR_INT("ptaa not defined", procName, 0);
1309  if ((n = ptaaGetCount(ptaa)) == 0)
1310  return ERROR_INT("ptaa empty", procName, 0);
1311  if (h <= 0)
1312  return ERROR_INT("invalid h", procName, 0);
1313  if (!pntop || !pnbot)
1314  return ERROR_INT("&ntop and &nbot not defined", procName, 0);
1315  if (!pytop || !pybot)
1316  return ERROR_INT("&ytop and &ybot not defined", procName, 0);
1317 
1318  na = numaCreate(n);
1319  for (i = 0; i < n; i++) {
1320  ptaaGetPt(ptaa, i, 0, NULL, &y);
1321  numaAddNumber(na, y);
1322  }
1323  numaSort(na, na, L_SORT_INCREASING);
1324  for (i = 0, ntop = 0; i < n; i++) {
1325  numaGetIValue(na, i, &iy);
1326  if (i == 0) ytop = iy;
1327  if (i == n - 1) ybot = iy;
1328  if (iy < 0.5 * h)
1329  ntop++;
1330  }
1331  numaDestroy(&na);
1332  nbot = n - ntop;
1333  *pntop = ntop;
1334  *pnbot = nbot;
1335  *pytop = ytop;
1336  *pybot = ybot;
1337  nmin = 4; /* minimum number of lines required in each half */
1338  both_halves = (ntop >= nmin) && (nbot >= nmin);
1339  fraction = (l_float32)(ybot - ytop) / (l_float32)h;
1340  if (both_halves && fraction > 0.50)
1341  return 1;
1342  return 0;
1343 }
1344 
1345 
1363 static l_int32
1365  l_float32 *pa,
1366  l_float32 *pb,
1367  l_float32 *pmederr)
1368 {
1369 l_int32 i, n;
1370 l_float32 x, y, xp, c0, c1;
1371 NUMA *naerr;
1372 
1373  PROCNAME("dewarpLinearLSF");
1374 
1375  if (pmederr) *pmederr = 0.0;
1376  if (!pa || !pb)
1377  return ERROR_INT("not all ptrs are defined", procName, 1);
1378  *pa = *pb = 0.0;
1379  if (!ptad)
1380  return ERROR_INT("ptad not defined", procName, 1);
1381 
1382  /* Fit to the longest lines */
1383  ptaGetLinearLSF(ptad, &c1, &c0, NULL);
1384  *pa = c1;
1385  *pb = c0;
1386 
1387  /* Optionally, find the median error */
1388  if (pmederr) {
1389  n = ptaGetCount(ptad);
1390  naerr = numaCreate(n);
1391  for (i = 0; i < n; i++) {
1392  ptaGetPt(ptad, i, &y, &xp);
1393  applyLinearFit(c1, c0, y, &x);
1394  numaAddNumber(naerr, L_ABS(x - xp));
1395  }
1396  numaGetMedian(naerr, pmederr);
1397  numaDestroy(&naerr);
1398  }
1399  return 0;
1400 }
1401 
1402 
1421 static l_int32
1423  l_float32 *pa,
1424  l_float32 *pb,
1425  l_float32 *pc,
1426  l_float32 *pmederr)
1427 {
1428 l_int32 i, n;
1429 l_float32 x, y, xp, c0, c1, c2;
1430 NUMA *naerr;
1431 
1432  PROCNAME("dewarpQuadraticLSF");
1433 
1434  if (pmederr) *pmederr = 0.0;
1435  if (!pa || !pb || !pc)
1436  return ERROR_INT("not all ptrs are defined", procName, 1);
1437  *pa = *pb = *pc = 0.0;
1438  if (!ptad)
1439  return ERROR_INT("ptad not defined", procName, 1);
1440 
1441  /* Fit to the longest lines */
1442  ptaGetQuadraticLSF(ptad, &c2, &c1, &c0, NULL);
1443  *pa = c2;
1444  *pb = c1;
1445  *pc = c0;
1446 
1447  /* Optionally, find the median error */
1448  if (pmederr) {
1449  n = ptaGetCount(ptad);
1450  naerr = numaCreate(n);
1451  for (i = 0; i < n; i++) {
1452  ptaGetPt(ptad, i, &y, &xp);
1453  applyQuadraticFit(c2, c1, c0, y, &x);
1454  numaAddNumber(naerr, L_ABS(x - xp));
1455  }
1456  numaGetMedian(naerr, pmederr);
1457  numaDestroy(&naerr);
1458  }
1459  return 0;
1460 }
1461 
1462 
1463 /*----------------------------------------------------------------------*
1464  * Build disparity model for slope near binding *
1465  *----------------------------------------------------------------------*/
1504 l_ok
1506  PIX *pixb,
1507  l_float32 fractthresh,
1508  l_int32 parity)
1509 {
1510 l_int32 i, j, x, n1, n2, nb, ne, count, w, h, ival, prev;
1511 l_int32 istart, iend, first, last, x0, x1, nx, ny;
1512 l_float32 fract, delta, sum, aveval, fval, del, denom;
1513 l_float32 ca, cb, cc, cd, ce, y;
1514 BOX *box;
1515 BOXA *boxa1, *boxa2;
1516 GPLOT *gplot;
1517 NUMA *na1, *na2, *na3, *na4, *nasum;
1518 PIX *pix1;
1519 PTA *pta1;
1520 FPIX *fpix;
1521 
1522  PROCNAME("dewarpFindHorizSlopeDisparity");
1523 
1524  if (!dew)
1525  return ERROR_INT("dew not defined", procName, 1);
1526  if (!dew->vvalid || !dew->hvalid)
1527  return ERROR_INT("invalid vert or horiz disparity model", procName, 1);
1528  if (!pixb || pixGetDepth(pixb) != 1)
1529  return ERROR_INT("pixb not defined or not 1 bpp", procName, 1);
1530 
1531  if (dew->debug) L_INFO("finding slope horizontal disparity\n", procName);
1532 
1533  /* Find the bounding boxes of the vertical strokes; remove noise */
1534  pix1 = pixMorphSequence(pixb, "o1.10", 0);
1535  pixDisplay(pix1, 100, 100);
1536  boxa1 = pixConnCompBB(pix1, 4);
1537  boxa2 = boxaSelectBySize(boxa1, 0, 5, L_SELECT_HEIGHT, L_SELECT_IF_GT,
1538  NULL);
1539  nb = boxaGetCount(boxa2);
1540  lept_stderr("number of components: %d\n", nb);
1541  boxaDestroy(&boxa1);
1542 
1543  /* Estimate the horizontal density of vertical strokes */
1544  na1 = numaCreate(0);
1545  numaSetParameters(na1, 0, 25);
1546  pixGetDimensions(pixb, &w, &h, NULL);
1547  for (x = 0; x + 50 < w; x += 25) {
1548  box = boxCreate(x, 0, 50, h);
1549  boxaContainedInBoxCount(boxa2, box, &count);
1550  numaAddNumber(na1, count);
1551  boxDestroy(&box);
1552  }
1553  if (dew->debug) {
1554  lept_mkdir("lept/dew");
1555  gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/dew/0091", NULL);
1556  lept_mv("/tmp/lept/dew/0091.png", "lept/dewmod", NULL, NULL);
1557  pixWriteDebug("/tmp/lept/dewmod/0090.png", pix1, IFF_PNG);
1558  }
1559  pixDestroy(&pix1);
1560  boxaDestroy(&boxa2);
1561 
1562  /* Find the left and right end local maxima; if the difference
1563  * is small, quit. */
1564  n1 = numaGetCount(na1);
1565  prev = 0;
1566  istart = 0;
1567  first = 0;
1568  for (i = 0; i < n1; i++) {
1569  numaGetIValue(na1, i, &ival);
1570  if (ival >= prev) {
1571  prev = ival;
1572  continue;
1573  } else {
1574  first = prev;
1575  istart = i - 1;
1576  break;
1577  }
1578  }
1579  prev = 0;
1580  last = 0;
1581  iend = n1 - 1;
1582  for (i = n1 - 1; i >= 0; i--) {
1583  numaGetIValue(na1, i, &ival);
1584  if (ival >= prev) {
1585  prev = ival;
1586  continue;
1587  } else {
1588  last = prev;
1589  iend = i + 1;
1590  break;
1591  }
1592  }
1593  na2 = numaClipToInterval(na1, istart, iend);
1594  numaDestroy(&na1);
1595  n2 = numaGetCount(na2);
1596  delta = (parity == 0) ? last - first : first - last;
1597  denom = L_MAX(1.0, (l_float32)(L_MIN(first, last)));
1598  fract = (l_float32)delta / denom;
1599  if (dew->debug) {
1600  L_INFO("Slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1601  procName, first, last, fract);
1602  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0092", NULL);
1603  lept_mv("/tmp/lept/dew/0092.png", "lept/dewmod", NULL, NULL);
1604  }
1605  if (fract < fractthresh) {
1606  L_INFO("Small slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1607  procName, first, last, fract);
1608  numaDestroy(&na2);
1609  return 0;
1610  }
1611 
1612  /* Find the density far from the binding, and normalize to 1. */
1613  ne = n2 - n2 % 2;
1614  if (parity == 0)
1615  numaGetSumOnInterval(na2, 0, ne / 2 - 1, &sum);
1616  else /* parity == 1 */
1617  numaGetSumOnInterval(na2, ne / 2, ne - 1, &sum);
1618  denom = L_MAX(1.0, (l_float32)(ne / 2));
1619  aveval = sum / denom;
1620  na3 = numaMakeConstant(aveval, n2);
1621  numaArithOp(na2, na2, na3, L_ARITH_DIVIDE);
1622  numaDestroy(&na3);
1623  if (dew->debug) {
1624  L_INFO("Average background density: %5.1f\n", procName, aveval);
1625  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0093", NULL);
1626  lept_mv("/tmp/lept/dew/0093.png", "lept/dewmod", NULL, NULL);
1627  }
1628 
1629  /* Fit the normalized density curve to a quartic */
1630  pta1 = numaConvertToPta1(na2);
1631  ptaWriteStream(stderr, pta1, 0);
1632 /* ptaGetQuadraticLSF(pta1, NULL, NULL, NULL, &na3); */
1633  ptaGetQuarticLSF(pta1, &ca, &cb, &cc, &cd, &ce, &na3);
1634  ptaGetArrays(pta1, &na4, NULL);
1635  if (dew->debug) {
1636  gplot = gplotSimpleXY1(na4, na3, GPLOT_LINES, GPLOT_PNG,
1637  "/tmp/lept/dew/0094", NULL);
1638  gplotDestroy(&gplot);
1639  lept_mv("/tmp/lept/dew/0094.png", "lept/dewmod", NULL, NULL);
1640  }
1641  ptaDestroy(&pta1);
1642 
1643  /* Integrate from the high point down to 1 (or v.v) to get the
1644  * disparity needed to make the density constant. */
1645  nasum = numaMakeConstant(0, w); /* area under the curve above 1.0 */
1646  if (parity == 0) {
1647  for (i = n2 - 1; i >= 0; i--) {
1648  numaGetFValue(na3, i, &fval);
1649  if (fval < 1.0) break;
1650  }
1651  numaGetIValue(na4, i + 1, &x0);
1652  numaGetIValue(na4, n2 - 1, &x1);
1653  numaSetParameters(nasum, x0, 1);
1654  sum = 0.0;
1655  for (x = x0; x < x1; x++) {
1656  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1657  sum += (y - 1.0);
1658  numaReplaceNumber(nasum, x, sum);
1659  }
1660  for (x = x1; x < w; x++)
1661  numaReplaceNumber(nasum, x, sum);
1662  } else { /* parity == 1 */
1663  for (i = 0; i < n2; i++) {
1664  numaGetFValue(na3, i, &fval);
1665  if (fval < 1.0) break;
1666  }
1667  numaGetIValue(na4, 0, &x0);
1668  numaGetIValue(na4, i - 1, &x1);
1669  numaSetParameters(nasum, x0, 1);
1670  sum = 0.0;
1671  for (x = x1; x >= x0; x--) {
1672  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1673  sum += (y - 1.0);
1674  numaReplaceNumber(nasum, x, sum);
1675  }
1676  for (x = x0; x >= 0; x--)
1677  numaReplaceNumber(nasum, x, sum);
1678  }
1679 
1680  /* Save the result in a fpix at the specified subsampling */
1681  nx = dew->nx;
1682  ny = dew->ny;
1683  fpix = fpixCreate(nx, ny);
1684  del = (l_float32)w / (l_float32)nx;
1685  for (i = 0; i < ny; i++) {
1686  for (j = 0; j < nx; j++) {
1687  x = del * j;
1688  numaGetFValue(nasum, x, &fval);
1689  fpixSetPixel(fpix, j, i, fval);
1690  }
1691  }
1692  dew->sampydispar = fpix;
1693  dew->ysuccess = 1;
1694 
1695  numaDestroy(&na2);
1696  numaDestroy(&na3);
1697  numaDestroy(&na4);
1698  numaDestroy(&nasum);
1699  return 0;
1700 }
1701 
1702 
1703 /*----------------------------------------------------------------------*
1704  * Build line disparity model *
1705  *----------------------------------------------------------------------*/
1734 l_ok
1736  l_int32 opensize,
1737  const char *debugfile)
1738 {
1739 char buf[64];
1740 l_int32 i, j, bx, by, ret, nlines;
1741 BOXA *boxa;
1742 PIX *pixs, *pixh, *pixv, *pix, *pix1, *pix2;
1743 PIXA *pixa1, *pixa2;
1744 PTA *pta;
1745 PTAA *ptaa1, *ptaa2;
1746 
1747  PROCNAME("dewarpBuildLineModel");
1748 
1749  if (!dew)
1750  return ERROR_INT("dew not defined", procName, 1);
1751  if (opensize < 3) {
1752  L_WARNING("opensize should be >= 3; setting to 8\n", procName);
1753  opensize = 8; /* default */
1754  }
1755 
1756  dew->debug = (debugfile) ? 1 : 0;
1757  dew->vsuccess = dew->hsuccess = 0;
1758  pixs = dew->pixs;
1759  if (debugfile) {
1760  lept_rmdir("lept/dewline"); /* erase previous images */
1761  lept_mkdir("lept/dewline");
1762  lept_rmdir("lept/dewmod"); /* erase previous images */
1763  lept_mkdir("lept/dewmod");
1764  lept_mkdir("lept/dewarp");
1765  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
1766  pixWriteDebug("/tmp/lept/dewline/001.png", pixs, IFF_PNG);
1767  }
1768 
1769  /* Extract and solidify the horizontal and vertical lines. We use
1770  * the horizontal lines to derive the vertical disparity, and v.v.
1771  * Both disparities are computed using the vertical disparity
1772  * algorithm; the horizontal disparity is found from the
1773  * vertical lines by rotating them clockwise by 90 degrees.
1774  * On the first pass, we compute the horizontal disparity, from
1775  * the vertical lines, by rotating them by 90 degrees (so they
1776  * are horizontal) and computing the vertical disparity on them;
1777  * we rotate the resulting fpix array for the horizontal disparity
1778  * back by -90 degrees. On the second pass, we compute the vertical
1779  * disparity from the horizontal lines in the usual fashion. */
1780  snprintf(buf, sizeof(buf), "d1.3 + c%d.1 + o%d.1", opensize - 2, opensize);
1781  pixh = pixMorphSequence(pixs, buf, 0); /* horiz */
1782  snprintf(buf, sizeof(buf), "d3.1 + c1.%d + o1.%d", opensize - 2, opensize);
1783  pix1 = pixMorphSequence(pixs, buf, 0); /* vert */
1784  pixv = pixRotateOrth(pix1, 1); /* vert rotated to horizontal */
1785  pixa1 = pixaCreate(2);
1786  pixaAddPix(pixa1, pixv, L_INSERT); /* get horizontal disparity first */
1787  pixaAddPix(pixa1, pixh, L_INSERT);
1788  pixDestroy(&pix1);
1789 
1790  /*--------------------------------------------------------------*/
1791  /* Process twice: first for horiz disparity, then for vert */
1792  /*--------------------------------------------------------------*/
1793  for (i = 0; i < 2; i++) {
1794  pix = pixaGetPix(pixa1, i, L_CLONE);
1795  pixDisplay(pix, 0, 900);
1796  boxa = pixConnComp(pix, &pixa2, 8);
1797  nlines = boxaGetCount(boxa);
1798  boxaDestroy(&boxa);
1799  if (nlines < dew->minlines) {
1800  L_WARNING("only found %d lines\n", procName, nlines);
1801  pixDestroy(&pix);
1802  pixaDestroy(&pixa1);
1803  continue;
1804  }
1805 
1806  /* Identify the pixels along the skeleton of each line */
1807  ptaa1 = ptaaCreate(nlines);
1808  for (j = 0; j < nlines; j++) {
1809  pixaGetBoxGeometry(pixa2, j, &bx, &by, NULL, NULL);
1810  pix1 = pixaGetPix(pixa2, j, L_CLONE);
1811  pta = dewarpGetMeanVerticals(pix1, bx, by);
1812  ptaaAddPta(ptaa1, pta, L_INSERT);
1813  pixDestroy(&pix1);
1814  }
1815  pixaDestroy(&pixa2);
1816  if (debugfile) {
1817  pix1 = pixConvertTo32(pix);
1818  pix2 = pixDisplayPtaa(pix1, ptaa1);
1819  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 2 + 2 * i);
1820  pixWriteDebug(buf, pix2, IFF_PNG);
1821  pixDestroy(&pix1);
1822  pixDestroy(&pix2);
1823  }
1824 
1825  /* Remove all lines that are not at least 0.75 times the length
1826  * of the longest line. */
1827  ptaa2 = dewarpRemoveShortLines(pix, ptaa1, 0.75, DEBUG_SHORT_LINES);
1828  if (debugfile) {
1829  pix1 = pixConvertTo32(pix);
1830  pix2 = pixDisplayPtaa(pix1, ptaa2);
1831  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 3 + 2 * i);
1832  pixWriteDebug(buf, pix2, IFF_PNG);
1833  pixDestroy(&pix1);
1834  pixDestroy(&pix2);
1835  }
1836  ptaaDestroy(&ptaa1);
1837  nlines = ptaaGetCount(ptaa2);
1838  if (nlines < dew->minlines) {
1839  pixDestroy(&pix);
1840  ptaaDestroy(&ptaa2);
1841  L_WARNING("%d lines: too few to build model\n", procName, nlines);
1842  continue;
1843  }
1844 
1845  /* Get the sampled 'vertical' disparity from the textline
1846  * centers. The disparity array will push pixels vertically
1847  * so that each line is flat and centered at the y-position
1848  * of the mid-point. */
1849  ret = dewarpFindVertDisparity(dew, ptaa2, 1 - i);
1850 
1851  /* If i == 0, move the result to the horizontal disparity,
1852  * rotating it back by -90 degrees. */
1853  if (i == 0) { /* horizontal disparity, really */
1854  if (ret) {
1855  L_WARNING("horizontal disparity not built\n", procName);
1856  } else {
1857  L_INFO("hsuccess = 1\n", procName);
1858  dew->samphdispar = fpixRotateOrth(dew->sampvdispar, 3);
1859  fpixDestroy(&dew->sampvdispar);
1860  if (debugfile)
1861  lept_mv("/tmp/lept/dewarp/vert_disparity.pdf",
1862  "lept/dewarp", "horiz_disparity.pdf", NULL);
1863  }
1864  dew->hsuccess = dew->vsuccess;
1865  dew->vsuccess = 0;
1866  } else { /* i == 1 */
1867  if (ret)
1868  L_WARNING("vertical disparity not built\n", procName);
1869  else
1870  L_INFO("vsuccess = 1\n", procName);
1871  }
1872  ptaaDestroy(&ptaa2);
1873  pixDestroy(&pix);
1874  }
1875  pixaDestroy(&pixa1);
1876 
1877  /* Debug output */
1878  if (debugfile) {
1879  if (dew->vsuccess == 1) {
1880  dewarpPopulateFullRes(dew, NULL, 0, 0);
1881  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
1882  pixWriteDebug("/tmp/lept/dewline/006.png", pix1, IFF_PNG);
1883  pixDisplay(pix1, 1000, 0);
1884  pixDestroy(&pix1);
1885  }
1886  if (dew->hsuccess == 1) {
1887  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
1888  pixWriteDebug("/tmp/lept/dewline/007.png", pix1, IFF_PNG);
1889  pixDisplay(pix1, 1000, 0);
1890  pixDestroy(&pix1);
1891  }
1892  convertFilesToPdf("/tmp/lept/dewline", NULL, 135, 1.0, 0, 0,
1893  "Dewarp Build Line Model", debugfile);
1894  lept_stderr("pdf file: %s\n", debugfile);
1895  }
1896 
1897  return 0;
1898 }
1899 
1900 
1901 /*----------------------------------------------------------------------*
1902  * Query model status *
1903  *----------------------------------------------------------------------*/
1918 l_ok
1920  l_int32 pageno,
1921  l_int32 *pvsuccess,
1922  l_int32 *phsuccess)
1923 {
1924 L_DEWARP *dew;
1925 
1926  PROCNAME("dewarpaModelStatus");
1927 
1928  if (pvsuccess) *pvsuccess = 0;
1929  if (phsuccess) *phsuccess = 0;
1930  if (!dewa)
1931  return ERROR_INT("dewa not defined", procName, 1);
1932 
1933  if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL)
1934  return ERROR_INT("dew not retrieved", procName, 1);
1935  if (pvsuccess) *pvsuccess = dew->vsuccess;
1936  if (phsuccess) *phsuccess = dew->hsuccess;
1937  return 0;
1938 }
1939 
1940 
1941 /*----------------------------------------------------------------------*
1942  * Rendering helpers *
1943  *----------------------------------------------------------------------*/
1952 static l_int32
1954  NUMA *namidys,
1955  l_int32 linew)
1956 {
1957 l_int32 i, n, w, yval, rval, gval, bval;
1958 PIXCMAP *cmap;
1959 
1960  PROCNAME("pixRenderMidYs");
1961 
1962  if (!pixs)
1963  return ERROR_INT("pixs not defined", procName, 1);
1964  if (!namidys)
1965  return ERROR_INT("namidys not defined", procName, 1);
1966 
1967  w = pixGetWidth(pixs);
1968  n = numaGetCount(namidys);
1969  cmap = pixcmapCreateRandom(8, 0, 0);
1970  for (i = 0; i < n; i++) {
1971  pixcmapGetColor(cmap, i % 256, &rval, &gval, &bval);
1972  numaGetIValue(namidys, i, &yval);
1973  pixRenderLineArb(pixs, 0, yval, w, yval, linew, rval, gval, bval);
1974  }
1975  pixcmapDestroy(&cmap);
1976  return 0;
1977 }
1978 
1979 
1989 static l_int32
1991  PTA *ptal,
1992  PTA *ptar,
1993  l_uint32 color)
1994 {
1995 PIX *pixcirc;
1996 PTA *ptalt, *ptart, *ptacirc;
1997 
1998  PROCNAME("pixRenderHorizEndPoints");
1999 
2000  if (!pixs)
2001  return ERROR_INT("pixs not defined", procName, 1);
2002  if (!ptal || !ptar)
2003  return ERROR_INT("ptal and ptar not both defined", procName, 1);
2004 
2005  ptacirc = generatePtaFilledCircle(5);
2006  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
2007  ptalt = ptaTranspose(ptal);
2008  ptart = ptaTranspose(ptar);
2009 
2010  pixDisplayPtaPattern(pixs, pixs, ptalt, pixcirc, 5, 5, color);
2011  pixDisplayPtaPattern(pixs, pixs, ptart, pixcirc, 5, 5, color);
2012  ptaDestroy(&ptacirc);
2013  ptaDestroy(&ptalt);
2014  ptaDestroy(&ptart);
2015  pixDestroy(&pixcirc);
2016  return 0;
2017 }
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:1828
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:255
struct FPix * sampvdispar
Definition: dewarp.h:155
l_int32 nlines
Definition: dewarp.h:169
l_ok ptaGetQuadraticLSF(PTA *pta, l_float32 *pa, l_float32 *pb, l_float32 *pc, NUMA **pnafit)
ptaGetQuadraticLSF()
Definition: ptafunc1.c:1207
struct FPix * samphdispar
Definition: dewarp.h:156
l_int32 lept_mv(const char *srcfile, const char *newdir, const char *newtail, char **pnewpath)
lept_mv()
Definition: utils2.c:2572
l_int32 debug
Definition: dewarp.h:187
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
static PTA * dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas)
dewarpRemoveBadEndPoints()
Definition: dewarp2.c:1216
l_int32 h
Definition: dewarp.h:164
l_ok ptaaGetPt(PTAA *ptaa, l_int32 ipta, l_int32 jpt, l_float32 *px, l_float32 *py)
ptaaGetPt()
Definition: ptabasic.c:1176
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218
l_ok ptaGetRankValue(PTA *pta, l_float32 fract, PTA *ptasort, l_int32 sorttype, l_float32 *pval)
ptaGetRankValue()
Definition: ptafunc2.c:265
NUMA * numaGetSortIndex(NUMA *na, l_int32 sortorder)
numaGetSortIndex()
Definition: numafunc1.c:2751
l_int32 ny
Definition: dewarp.h:177
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:700
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixGenerateFromPta(PTA *pta, l_int32 w, l_int32 h)
pixGenerateFromPta()
Definition: ptafunc1.c:2023
GPLOT * gplotSimpleXY1(NUMA *nax, NUMA *nay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title)
gplotSimpleXY1()
Definition: gplot.c:915
NUMA * numaSortByIndex(NUMA *nas, NUMA *naindex)
numaSortByIndex()
Definition: numafunc1.c:2916
l_ok ptaWriteStream(FILE *fp, PTA *pta, l_int32 type)
ptaWriteStream()
Definition: ptabasic.c:873
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:343
Definition: pix.h:713
l_ok numaWriteDebug(const char *filename, NUMA *na)
numaWriteDebug()
Definition: numabasic.c:1224
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_ok boxaContainedInBoxCount(BOXA *boxa, BOX *box, l_int32 *pcount)
boxaContainedInBoxCount()
Definition: boxfunc1.c:234
PTAA * dewarpRemoveShortLines(PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag)
dewarpRemoveShortLines()
Definition: dewarp2.c:968
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:220
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:1450
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:120
l_ok numaGetMedian(NUMA *na, l_float32 *pval)
numaGetMedian()
Definition: numafunc1.c:3405
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
l_int32 sampling
Definition: dewarp.h:166
PIX * pixDisplayPtaaPattern(PIX *pixd, PIX *pixs, PTAA *ptaa, PIX *pixp, l_int32 cx, l_int32 cy)
pixDisplayPtaaPattern()
Definition: ptafunc1.c:2492
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:851
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
Definition: pix.h:710
NUMA * numaSort(NUMA *naout, NUMA *nain, l_int32 sortorder)
numaSort()
Definition: numafunc1.c:2650
struct Numa * namidys
Definition: dewarp.h:161
l_int32 leftcurv
Definition: dewarp.h:174
static PTA * dewarpGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y)
dewarpGetMeanVerticals()
Definition: dewarp2.c:922
l_ok ptaGetArrays(PTA *pta, NUMA **pnax, NUMA **pnay)
ptaGetArrays()
Definition: ptabasic.c:639
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:279
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:527
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:583
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
PTA * ptaSort(PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex)
ptaSort()
Definition: ptafunc2.c:97
l_int32 ysuccess
Definition: dewarp.h:182
l_ok dewarpBuildLineModel(L_DEWARP *dew, l_int32 opensize, const char *debugfile)
dewarpBuildLineModel()
Definition: dewarp2.c:1735
PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h)
pixaDisplay()
Definition: pixafunc2.c:191
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1182
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:253
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
l_ok ptaGetLinearLSF(PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit)
ptaGetLinearLSF()
Definition: ptafunc1.c:1106
Definition: pix.h:491
l_int32 pageno
Definition: dewarp.h:165
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:167
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:600
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:1536
struct L_Dewarpa * dewa
Definition: dewarp.h:153
l_ok dewarpBuildPageModel(L_DEWARP *dew, const char *debugfile)
dewarpBuildPageModel()
Definition: dewarp2.c:156
PTAA * ptaaSortByIndex(PTAA *ptaas, NUMA *naindex)
ptaaSortByIndex()
Definition: ptafunc2.c:226
struct Pix * pixs
Definition: dewarp.h:154
l_ok applyQuadraticFit(l_float32 a, l_float32 b, l_float32 c, l_float32 x, l_float32 *py)
applyQuadraticFit()
Definition: ptafunc1.c:1777
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:151
l_ok dewarpFindHorizSlopeDisparity(L_DEWARP *dew, PIX *pixb, l_float32 fractthresh, l_int32 parity)
dewarpFindHorizSlopeDisparity()
Definition: dewarp2.c:1505
NUMA * numaArithOp(NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op)
numaArithOp()
Definition: numafunc1.c:173
l_ok dewarpFindVertDisparity(L_DEWARP *dew, PTAA *ptaa, l_int32 rotflag)
dewarpFindVertDisparity()
Definition: dewarp2.c:303
PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag)
ptaaGetPta()
Definition: ptabasic.c:1145
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
PTA * numaConvertToPta1(NUMA *na)
numaConvertToPta1()
Definition: ptafunc1.c:2309
Definition: array.h:70
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1688
PIX * pixDisplayPtaa(PIX *pixs, PTAA *ptaa)
pixDisplayPtaa()
Definition: ptafunc1.c:2672
l_int32 minlines
Definition: dewarp.h:168
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:310
l_ok dewarpFindHorizDisparity(L_DEWARP *dew, PTAA *ptaa)
dewarpFindHorizDisparity()
Definition: dewarp2.c:567
Definition: pix.h:530
l_ok dewarpaModelStatus(L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess)
dewarpaModelStatus()
Definition: dewarp2.c:1919
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:824
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
void ptaaDestroy(PTAA **pptaa)
ptaaDestroy()
Definition: ptabasic.c:1003
FPIX * fpixRotateOrth(FPIX *fpixs, l_int32 quads)
fpixRotateOrth()
Definition: fpix2.c:1756
PIXA * pixaSelectBySize(PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixaSelectBySize()
Definition: pixafunc1.c:306
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:548
PTA * ptaCreateFromNuma(NUMA *nax, NUMA *nay)
ptaCreateFromNuma()
Definition: ptabasic.c:152
l_int32 mincurv
Definition: dewarp.h:170
static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal, PTA **pptar)
dewarpGetLineEndPoints()
Definition: dewarp2.c:1051
l_ok dewarpPopulateFullRes(L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y)
dewarpPopulateFullRes()
Definition: dewarp3.c:787
static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1, PTA **pptal2, PTA **pptar2)
dewarpFilterLineEndPoints()
Definition: dewarp2.c:1130
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:993
Definition: gplot.h:76
l_int32 vvalid
Definition: dewarp.h:183
l_ok pixaGetBoxGeometry(PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
pixaGetBoxGeometry()
Definition: pixabasic.c:854
l_int32 rightcurv
Definition: dewarp.h:175
PTAA * dewarpGetTextlineCenters(PIX *pixs, l_int32 debugflag)
dewarpGetTextlineCenters()
Definition: dewarp2.c:814
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
struct FPix * sampydispar
Definition: dewarp.h:157
PIX * pixRotateOrth(PIX *pixs, l_int32 quads)
pixRotateOrth()
Definition: rotateorth.c:75
Definition: pix.h:711
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:1294
l_int32 maxcurv
Definition: dewarp.h:171
l_ok ptaGetRange(PTA *pta, l_float32 *pminx, l_float32 *pmaxx, l_float32 *pminy, l_float32 *pmaxy)
ptaGetRange()
Definition: ptafunc1.c:488
Definition: pix.h:455
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_int32 vsuccess
Definition: dewarp.h:180
PIX * pixSeedfillBinary(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillBinary()
Definition: seedfill.c:247
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
l_int32 max_edgecurv
Definition: dewarp.h:135
l_ok numaGetMin(NUMA *na, l_float32 *pminval, l_int32 *piminloc)
numaGetMin()
Definition: numafunc1.c:453
l_ok applyLinearFit(l_float32 a, l_float32 b, l_float32 x, l_float32 *py)
applyLinearFit()
Definition: ptafunc1.c:1753
l_int32 hsuccess
Definition: dewarp.h:181
l_int32 ptaaGetCount(PTAA *ptaa)
ptaaGetCount()
Definition: ptabasic.c:1125
static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pmederr)
dewarpQuadraticLSF()
Definition: dewarp2.c:1422
l_int32 hvalid
Definition: dewarp.h:184
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:156
l_int32 leftslope
Definition: dewarp.h:172
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:892
l_ok ptaWriteDebug(const char *filename, PTA *pta, l_int32 type)
ptaWriteDebug()
Definition: ptabasic.c:816
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
PIX * pixDisplayPta(PIX *pixd, PIX *pixs, PTA *pta)
pixDisplayPta()
Definition: ptafunc1.c:2426
struct FPix * fullvdispar
Definition: dewarp.h:158
Definition: pix.h:138
l_ok numaGetMedianDevFromMedian(NUMA *na, l_float32 *pmed, l_float32 *pdev)
numaGetMedianDevFromMedian()
Definition: numafunc1.c:3511
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:195
PTA * ptaSelectRange(PTA *ptas, l_int32 first, l_int32 last)
ptaSelectRange()
Definition: ptafunc1.c:389
l_ok numaReplaceNumber(NUMA *na, l_int32 index, l_float32 val)
numaReplaceNumber()
Definition: numabasic.c:627
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
l_int32 rightslope
Definition: dewarp.h:173
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:734
l_ok ptaGetMinMax(PTA *pta, l_float32 *pxmin, l_float32 *pymin, l_float32 *pxmax, l_float32 *pymax)
ptaGetMinMax()
Definition: ptafunc1.c:918
struct FPix * fullhdispar
Definition: dewarp.h:159
PIX * pixDisplayPtaPattern(PIX *pixd, PIX *pixs, PTA *pta, PIX *pixp, l_int32 cx, l_int32 cy, l_uint32 color)
pixDisplayPtaPattern()
Definition: ptafunc1.c:2559
l_int32 nx
Definition: dewarp.h:176
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:578
l_ok ptaaAddPta(PTAA *ptaa, PTA *pta, l_int32 copyflag)
ptaaAddPta()
Definition: ptabasic.c:1038
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:496
static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar, l_uint32 color)
pixRenderHorizEndPoints()
Definition: dewarp2.c:1990
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2295
l_int32 w
Definition: dewarp.h:163
static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew)
pixRenderMidYs()
Definition: dewarp2.c:1953
PTA * generatePtaFilledCircle(l_int32 radius)
generatePtaFilledCircle()
Definition: graphics.c:833
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:292
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
struct Numa * nacurves
Definition: dewarp.h:162
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
l_ok numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:613
PTAA * ptaaCreate(l_int32 n)
ptaaCreate()
Definition: ptabasic.c:976
L_DEWARP * dewarpaGetDewarp(L_DEWARPA *dewa, l_int32 index)
dewarpaGetDewarp()
Definition: dewarp1.c:911
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:665
PTA * ptaTranspose(PTA *ptas)
ptaTranspose()
Definition: ptafunc1.c:293
Definition: pix.h:516
Definition: pix.h:578
l_ok ptaaWriteDebug(const char *filename, PTAA *ptaa, l_int32 type)
ptaaWriteDebug()
Definition: ptabasic.c:1476
static l_int32 dewarpLinearLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, l_float32 *pmederr)
dewarpLinearLSF()
Definition: dewarp2.c:1364
PIXCMAP * pixcmapCreateRandom(l_int32 depth, l_int32 hasblack, l_int32 haswhite)
pixcmapCreateRandom()
Definition: colormap.c:172
PIX * fpixRenderContours(FPIX *fpixs, l_float32 incr, l_float32 proxim)
fpixRenderContours()
Definition: graphics.c:2811