Leptonica  1.77.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 
61 #include <math.h>
62 #include "allheaders.h"
63 
64 static PTA *dewarpGetMeanVerticals(PIX *pixs, l_int32 x, l_int32 y);
65 static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal,
66  PTA **pptar);
67 static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1,
68  PTA **pptal2, PTA **pptar2);
69 static PTA *dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas);
70 static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h,
71  l_int32 *ptopline, l_int32 *pbotline);
72 static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb,
73  l_float32 *pc, l_float32 *pmederr);
74 static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew);
75 static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar,
76  l_uint32 color);
77 
78 
79 #ifndef NO_CONSOLE_IO
80 #define DEBUG_TEXTLINE_CENTERS 0 /* set this to 1 for debugging */
81 #define DEBUG_SHORT_LINES 0 /* ditto */
82 #else
83 #define DEBUG_TEXTLINE_CENTERS 0 /* always must be 0 */
84 #define DEBUG_SHORT_LINES 0 /* ditto */
85 #endif /* !NO_CONSOLE_IO */
86 
87  /* Special parameter values for reducing horizontal disparity */
88 static const l_float32 L_MIN_RATIO_LINES_TO_HEIGHT = 0.45;
89 static const l_int32 L_MIN_LINES_FOR_HORIZ_1 = 10; /* initially */
90 static const l_int32 L_MIN_LINES_FOR_HORIZ_2 = 3; /* after, in each half */
91 static const l_float32 L_ALLOWED_W_FRACT = 0.05; /* no bigger */
92 
93 
94 /*----------------------------------------------------------------------*
95  * Build basic page disparity model *
96  *----------------------------------------------------------------------*/
147 l_ok
149  const char *debugfile)
150 {
151 l_int32 linecount, topline, botline, ret;
152 PIX *pixs, *pix1, *pix2, *pix3;
153 PTA *pta;
154 PTAA *ptaa1, *ptaa2;
155 
156  PROCNAME("dewarpBuildPageModel");
157 
158  if (!dew)
159  return ERROR_INT("dew not defined", procName, 1);
160 
161  dew->debug = (debugfile) ? 1 : 0;
162  dew->vsuccess = dew->hsuccess = 0;
163  pixs = dew->pixs;
164  if (debugfile) {
165  lept_rmdir("lept/dewmod"); /* erase previous images */
166  lept_mkdir("lept/dewmod");
167  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
168  pixWriteDebug("/tmp/lept/dewmod/0010.png", pixs, IFF_PNG);
169  }
170 
171  /* Make initial estimate of centers of textlines */
172  ptaa1 = dewarpGetTextlineCenters(pixs, debugfile || DEBUG_TEXTLINE_CENTERS);
173  if (!ptaa1) {
174  L_WARNING("textline centers not found; model not built\n", procName);
175  return 1;
176  }
177  if (debugfile) {
178  pix1 = pixConvertTo32(pixs);
179  pta = generatePtaFilledCircle(1);
180  pix2 = pixGenerateFromPta(pta, 5, 5);
181  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa1, pix2, 2, 2);
182  pixWriteDebug("/tmp/lept/dewmod/0020.png", pix3, IFF_PNG);
183  pixDestroy(&pix1);
184  pixDestroy(&pix2);
185  pixDestroy(&pix3);
186  ptaDestroy(&pta);
187  }
188 
189  /* Remove all lines that are not at least 0.8 times the length
190  * of the longest line. */
191  ptaa2 = dewarpRemoveShortLines(pixs, ptaa1, 0.8,
192  debugfile || DEBUG_SHORT_LINES);
193  if (debugfile) {
194  pix1 = pixConvertTo32(pixs);
195  pta = generatePtaFilledCircle(1);
196  pix2 = pixGenerateFromPta(pta, 5, 5);
197  pix3 = pixDisplayPtaaPattern(NULL, pix1, ptaa2, pix2, 2, 2);
198  pixWriteDebug("/tmp/lept/dewmod/0030.png", pix3, IFF_PNG);
199  pixDestroy(&pix1);
200  pixDestroy(&pix2);
201  pixDestroy(&pix3);
202  ptaDestroy(&pta);
203  }
204  ptaaDestroy(&ptaa1);
205 
206  /* Verify that there are sufficient "long" lines */
207  linecount = ptaaGetCount(ptaa2);
208  if (linecount < dew->minlines) {
209  ptaaDestroy(&ptaa2);
210  L_WARNING("linecount %d < min req'd number of lines (%d) for model\n",
211  procName, linecount, dew->minlines);
212  return 1;
213  }
214 
215  /* Verify that the lines have a reasonable coverage of the
216  * vertical extent of the image foreground. */
217  if (dewarpIsLineCoverageValid(ptaa2, pixGetHeight(pixs),
218  &topline, &botline) == FALSE) {
219  ptaaDestroy(&ptaa2);
220  L_WARNING("invalid line coverage: [%d ... %d] in height %d\n",
221  procName, topline, botline, pixGetHeight(pixs));
222  return 1;
223  }
224 
225  /* Get the sampled vertical disparity from the textline centers.
226  * The disparity array will push pixels vertically so that each
227  * textline is flat and centered at the y-position of the mid-point. */
228  if (dewarpFindVertDisparity(dew, ptaa2, 0) != 0) {
229  L_WARNING("vertical disparity not built\n", procName);
230  ptaaDestroy(&ptaa2);
231  return 1;
232  }
233 
234  /* Get the sampled horizontal disparity from the left and right
235  * edges of the text. The disparity array will expand the image
236  * linearly outward to align the text edges vertically.
237  * Do this even if useboth == 0; we still calculate it even
238  * if we don't plan to use it. */
239  if ((ret = dewarpFindHorizDisparity(dew, ptaa2)) == 0)
240  L_INFO("hsuccess = 1\n", procName);
241 
242  /* Debug output */
243  if (debugfile) {
244  dewarpPopulateFullRes(dew, NULL, 0, 0);
245  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
246  pixWriteDebug("/tmp/lept/dewmod/0060.png", pix1, IFF_PNG);
247  pixDisplay(pix1, 1000, 0);
248  pixDestroy(&pix1);
249  if (ret == 0) {
250  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
251  pixWriteDebug("/tmp/lept/dewmod/0070.png", pix1, IFF_PNG);
252  pixDisplay(pix1, 1000, 0);
253  pixDestroy(&pix1);
254  }
255  convertFilesToPdf("/tmp/lept/dewmod", NULL, 135, 1.0, 0, 0,
256  "Dewarp Build Model", debugfile);
257  fprintf(stderr, "pdf file: %s\n", debugfile);
258  }
259 
260  ptaaDestroy(&ptaa2);
261  return 0;
262 }
263 
264 
293 l_ok
295  PTAA *ptaa,
296  l_int32 rotflag)
297 {
298 l_int32 i, j, nlines, npts, nx, ny, sampling;
299 l_float32 c0, c1, c2, x, y, midy, val, medval, meddev, minval, maxval;
300 l_float32 *famidys;
301 NUMA *nax, *nafit, *nacurve0, *nacurve1, *nacurves;
302 NUMA *namidy, *namidys, *namidysi;
303 PIX *pix1, *pix2, *pixcirc, *pixdb;
304 PTA *pta, *ptad, *ptacirc;
305 PTAA *ptaa0, *ptaa1, *ptaa2, *ptaa3, *ptaa4, *ptaa5, *ptaat;
306 FPIX *fpix;
307 
308  PROCNAME("dewarpFindVertDisparity");
309 
310  if (!dew)
311  return ERROR_INT("dew not defined", procName, 1);
312  dew->vsuccess = 0;
313  if (!ptaa)
314  return ERROR_INT("ptaa not defined", procName, 1);
315 
316  if (dew->debug) L_INFO("finding vertical disparity\n", procName);
317 
318  /* Do quadratic fit to smooth each line. A single quadratic
319  * over the entire width of the line appears to be sufficient.
320  * Quartics tend to overfit to noise. Each line is thus
321  * represented by three coefficients: y(x) = c2 * x^2 + c1 * x + c0.
322  * Using the coefficients, sample each fitted curve uniformly
323  * across the full width of the image. The result is in ptaa0. */
324  sampling = dew->sampling;
325  nx = (rotflag) ? dew->ny : dew->nx;
326  ny = (rotflag) ? dew->nx : dew->ny;
327  nlines = ptaaGetCount(ptaa);
328  dew->nlines = nlines;
329  ptaa0 = ptaaCreate(nlines);
330  nacurve0 = numaCreate(nlines); /* stores curvature coeff c2 */
331  pixdb = (rotflag) ? pixRotateOrth(dew->pixs, 1) : pixClone(dew->pixs);
332  for (i = 0; i < nlines; i++) { /* for each line */
333  pta = ptaaGetPta(ptaa, i, L_CLONE);
334  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
335  numaAddNumber(nacurve0, c2);
336  ptad = ptaCreate(nx);
337  for (j = 0; j < nx; j++) { /* uniformly sampled in x */
338  x = j * sampling;
339  applyQuadraticFit(c2, c1, c0, x, &y);
340  ptaAddPt(ptad, x, y);
341  }
342  ptaaAddPta(ptaa0, ptad, L_INSERT);
343  ptaDestroy(&pta);
344  }
345  if (dew->debug) {
346  lept_mkdir("lept/dewarp");
347  lept_mkdir("lept/dewdebug");
348  lept_mkdir("lept/dewmod");
349  ptaat = ptaaCreate(nlines);
350  for (i = 0; i < nlines; i++) {
351  pta = ptaaGetPta(ptaa, i, L_CLONE);
352  ptaGetArrays(pta, &nax, NULL);
353  ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit);
354  ptad = ptaCreateFromNuma(nax, nafit);
355  ptaaAddPta(ptaat, ptad, L_INSERT);
356  ptaDestroy(&pta);
357  numaDestroy(&nax);
358  numaDestroy(&nafit);
359  }
360  pix1 = pixConvertTo32(pixdb);
361  pta = generatePtaFilledCircle(1);
362  pixcirc = pixGenerateFromPta(pta, 5, 5);
363  pix2 = pixDisplayPtaaPattern(NULL, pix1, ptaat, pixcirc, 2, 2);
364  pixWriteDebug("/tmp/lept/dewmod/0041.png", pix2, IFF_PNG);
365  pixDestroy(&pix1);
366  pixDestroy(&pix2);
367  ptaDestroy(&pta);
368  pixDestroy(&pixcirc);
369  ptaaDestroy(&ptaat);
370  }
371 
372  /* Remove lines with outlier curvatures.
373  * Note that this is just looking for internal consistency in
374  * the line curvatures. It is not rejecting lines based on
375  * the magnitude of the curvature. That is done when constraints
376  * are applied for valid models. */
377  numaGetMedianDevFromMedian(nacurve0, &medval, &meddev);
378  L_INFO("\nPage %d\n", procName, dew->pageno);
379  L_INFO("Pass 1: Curvature: medval = %f, meddev = %f\n",
380  procName, medval, meddev);
381  ptaa1 = ptaaCreate(nlines);
382  nacurve1 = numaCreate(nlines);
383  for (i = 0; i < nlines; i++) { /* for each line */
384  numaGetFValue(nacurve0, i, &val);
385  if (L_ABS(val - medval) > 7.0 * meddev) /* TODO: reduce to ~ 3.0 */
386  continue;
387  pta = ptaaGetPta(ptaa0, i, L_CLONE);
388  ptaaAddPta(ptaa1, pta, L_INSERT);
389  numaAddNumber(nacurve1, val);
390  }
391  nlines = ptaaGetCount(ptaa1);
392  numaDestroy(&nacurve0);
393 
394  /* Save the min and max curvature (in micro-units) */
395  numaGetMin(nacurve1, &minval, NULL);
396  numaGetMax(nacurve1, &maxval, NULL);
397  dew->mincurv = lept_roundftoi(1000000. * minval);
398  dew->maxcurv = lept_roundftoi(1000000. * maxval);
399  L_INFO("Pass 2: Min/max curvature = (%d, %d)\n", procName,
400  dew->mincurv, dew->maxcurv);
401 
402  /* Find and save the y values at the mid-points in each curve.
403  * If the slope is zero anywhere, it will typically be here. */
404  namidy = numaCreate(nlines);
405  for (i = 0; i < nlines; i++) {
406  pta = ptaaGetPta(ptaa1, i, L_CLONE);
407  npts = ptaGetCount(pta);
408  ptaGetPt(pta, npts / 2, NULL, &midy);
409  numaAddNumber(namidy, midy);
410  ptaDestroy(&pta);
411  }
412 
413  /* Sort the lines in ptaa1c by their vertical position, going down */
414  namidysi = numaGetSortIndex(namidy, L_SORT_INCREASING);
415  namidys = numaSortByIndex(namidy, namidysi);
416  nacurves = numaSortByIndex(nacurve1, namidysi);
417  numaDestroy(&dew->namidys); /* in case previously made */
418  numaDestroy(&dew->nacurves);
419  dew->namidys = namidys;
420  dew->nacurves = nacurves;
421  ptaa2 = ptaaSortByIndex(ptaa1, namidysi);
422  numaDestroy(&namidy);
423  numaDestroy(&nacurve1);
424  numaDestroy(&namidysi);
425  if (dew->debug) {
426  numaWriteDebug("/tmp/lept/dewdebug/midys.na", namidys);
427  numaWriteDebug("/tmp/lept/dewdebug/curves.na", nacurves);
428  pix1 = pixConvertTo32(pixdb);
429  ptacirc = generatePtaFilledCircle(5);
430  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
431  srand(3);
432  pixDisplayPtaaPattern(pix1, pix1, ptaa2, pixcirc, 5, 5);
433  srand(3); /* use the same colors for text and reference lines */
434  pixRenderMidYs(pix1, namidys, 2);
435  pix2 = (rotflag) ? pixRotateOrth(pix1, 3) : pixClone(pix1);
436  pixWriteDebug("/tmp/lept/dewmod/0042.png", pix2, IFF_PNG);
437  pixDisplay(pix2, 0, 0);
438  ptaDestroy(&ptacirc);
439  pixDestroy(&pixcirc);
440  pixDestroy(&pix1);
441  pixDestroy(&pix2);
442  }
443  pixDestroy(&pixdb);
444 
445  /* Convert the sampled points in ptaa2 to a sampled disparity with
446  * with respect to the y value at the mid point in the curve.
447  * The disparity is the distance the point needs to move;
448  * plus is downward. */
449  ptaa3 = ptaaCreate(nlines);
450  for (i = 0; i < nlines; i++) {
451  pta = ptaaGetPta(ptaa2, i, L_CLONE);
452  numaGetFValue(namidys, i, &midy);
453  ptad = ptaCreate(nx);
454  for (j = 0; j < nx; j++) {
455  ptaGetPt(pta, j, &x, &y);
456  ptaAddPt(ptad, x, midy - y);
457  }
458  ptaaAddPta(ptaa3, ptad, L_INSERT);
459  ptaDestroy(&pta);
460  }
461  if (dew->debug) {
462  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa3.ptaa", ptaa3, 0);
463  }
464 
465  /* Generate ptaa4 by taking vertical 'columns' from ptaa3.
466  * We want to fit the vertical disparity on the column to the
467  * vertical position of the line, which we call 'y' here and
468  * obtain from namidys. So each pta in ptaa4 is the set of
469  * vertical disparities down a column of points. The columns
470  * in ptaa4 are equally spaced in x. */
471  ptaa4 = ptaaCreate(nx);
472  famidys = numaGetFArray(namidys, L_NOCOPY);
473  for (j = 0; j < nx; j++) {
474  pta = ptaCreate(nlines);
475  for (i = 0; i < nlines; i++) {
476  y = famidys[i];
477  ptaaGetPt(ptaa3, i, j, NULL, &val); /* disparity value */
478  ptaAddPt(pta, y, val);
479  }
480  ptaaAddPta(ptaa4, pta, L_INSERT);
481  }
482  if (dew->debug) {
483  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa4.ptaa", ptaa4, 0);
484  }
485 
486  /* Do quadratic fit vertically on each of the pixel columns
487  * in ptaa4, for the vertical displacement (which identifies the
488  * src pixel(s) for each dest pixel) as a function of y (the
489  * y value of the mid-points for each line). Then generate
490  * ptaa5 by sampling the fitted vertical displacement on a
491  * regular grid in the vertical direction. Each pta in ptaa5
492  * gives the vertical displacement for regularly sampled y values
493  * at a fixed x. */
494  ptaa5 = ptaaCreate(nx); /* uniformly sampled across full height of image */
495  for (j = 0; j < nx; j++) { /* for each column */
496  pta = ptaaGetPta(ptaa4, j, L_CLONE);
497  ptaGetQuadraticLSF(pta, &c2, &c1, &c0, NULL);
498  ptad = ptaCreate(ny);
499  for (i = 0; i < ny; i++) { /* uniformly sampled in y */
500  y = i * sampling;
501  applyQuadraticFit(c2, c1, c0, y, &val);
502  ptaAddPt(ptad, y, val);
503  }
504  ptaaAddPta(ptaa5, ptad, L_INSERT);
505  ptaDestroy(&pta);
506  }
507  if (dew->debug) {
508  ptaaWriteDebug("/tmp/lept/dewdebug/ptaa5.ptaa", ptaa5, 0);
509  convertFilesToPdf("/tmp/lept/dewmod", "004", 135, 1.0, 0, 0,
510  "Dewarp Vert Disparity",
511  "/tmp/lept/dewarp/vert_disparity.pdf");
512  fprintf(stderr, "pdf file: /tmp/lept/dewarp/vert_disparity.pdf\n");
513  }
514 
515  /* Save the result in a fpix at the specified subsampling */
516  fpix = fpixCreate(nx, ny);
517  for (i = 0; i < ny; i++) {
518  for (j = 0; j < nx; j++) {
519  ptaaGetPt(ptaa5, j, i, NULL, &val);
520  fpixSetPixel(fpix, j, i, val);
521  }
522  }
523  dew->sampvdispar = fpix;
524  dew->vsuccess = 1;
525 
526  ptaaDestroy(&ptaa0);
527  ptaaDestroy(&ptaa1);
528  ptaaDestroy(&ptaa2);
529  ptaaDestroy(&ptaa3);
530  ptaaDestroy(&ptaa4);
531  ptaaDestroy(&ptaa5);
532  return 0;
533 }
534 
535 
557 l_ok
559  PTAA *ptaa)
560 {
561 l_int32 i, j, h, nx, ny, sampling, ret;
562 l_float32 c0, c1, cl0, cl1, cl2, cr0, cr1, cr2;
563 l_float32 x, y, refl, refr;
564 l_float32 val, mederr;
565 NUMA *nald, *nard;
566 PIX *pix1;
567 PTA *ptal1, *ptar1; /* left/right end points of lines; initial */
568 PTA *ptal2, *ptar2; /* left/right end points; after filtering */
569 PTA *ptal3, *ptar3; /* left and right block, fitted, uniform spacing */
570 PTA *pta, *ptat, *pta1, *pta2;
571 PTAA *ptaah;
572 FPIX *fpix;
573 
574  PROCNAME("dewarpFindHorizDisparity");
575 
576  if (!dew)
577  return ERROR_INT("dew not defined", procName, 1);
578  dew->hsuccess = 0;
579  if (!ptaa)
580  return ERROR_INT("ptaa not defined", procName, 1);
581 
582  if (dew->debug) L_INFO("finding horizontal disparity\n", procName);
583 
584  /* Get the endpoints of the lines, and sort from top to bottom */
585  h = pixGetHeight(dew->pixs);
586  ret = dewarpGetLineEndPoints(h, ptaa, &ptal1, &ptar1);
587  if (ret) {
588  L_INFO("Horiz disparity not built\n", procName);
589  return 1;
590  }
591  if (dew->debug) {
592  lept_mkdir("lept/dewdebug");
593  lept_mkdir("lept/dewarp");
594  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left1.pta", ptal1, 1);
595  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right1.pta", ptar1, 1);
596  }
597 
598  /* Filter the points by x-location to prevent 2-column images
599  * from getting confused about left and right endpoints. We
600  * require valid left points to not be farther than
601  * 0.20 * (remaining distance to the right edge of the image)
602  * to the right of the leftmost endpoint, and similarly for
603  * the right endpoints. (Note: x and y are reversed in the pta.)
604  * Also require end points to be near the medians in the
605  * upper and lower halves. */
606  ret = dewarpFilterLineEndPoints(dew, ptal1, ptar1, &ptal2, &ptar2);
607  ptaDestroy(&ptal1);
608  ptaDestroy(&ptar1);
609 
610  /* Do a quadratic fit to the left and right endpoints of the
611  * longest lines. Each line is represented by 3 coefficients:
612  * x(y) = c2 * y^2 + c1 * y + c0.
613  * Using the coefficients, sample each fitted curve uniformly
614  * along the full height of the image. */
615  sampling = dew->sampling;
616  nx = dew->nx;
617  ny = dew->ny;
618 
619  /* Fit the left side, using quadratic LSF on the set of long
620  * lines. It is not necessary to use the noisy LSF fit
621  * function, because we've removed outlier end points by
622  * selecting the long lines. Then uniformly sample along
623  * this fitted curve. */
624  dewarpQuadraticLSF(ptal2, &cl2, &cl1, &cl0, &mederr);
625  dew->leftslope = lept_roundftoi(1000. * cl1); /* milli-units */
626  dew->leftcurv = lept_roundftoi(1000000. * cl2); /* micro-units */
627  L_INFO("Left quad LSF median error = %5.2f\n", procName, mederr);
628  L_INFO("Left edge slope = %d\n", procName, dew->leftslope);
629  L_INFO("Left edge curvature = %d\n", procName, dew->leftcurv);
630  ptal3 = ptaCreate(ny);
631  for (i = 0; i < ny; i++) { /* uniformly sampled in y */
632  y = i * sampling;
633  applyQuadraticFit(cl2, cl1, cl0, y, &x);
634  ptaAddPt(ptal3, x, y);
635  }
636 
637  /* Fit the right side in the same way. */
638  dewarpQuadraticLSF(ptar2, &cr2, &cr1, &cr0, &mederr);
639  dew->rightslope = lept_roundftoi(1000.0 * cr1); /* milli-units */
640  dew->rightcurv = lept_roundftoi(1000000. * cr2); /* micro-units */
641  L_INFO("Right quad LSF median error = %5.2f\n", procName, mederr);
642  L_INFO("Right edge slope = %d\n", procName, dew->rightslope);
643  L_INFO("Right edge curvature = %d\n", procName, dew->rightcurv);
644  ptar3 = ptaCreate(ny);
645  for (i = 0; i < ny; i++) { /* uniformly sampled in y */
646  y = i * sampling;
647  applyQuadraticFit(cr2, cr1, cr0, y, &x);
648  ptaAddPt(ptar3, x, y);
649  }
650 
651  if (dew->debug) {
652  PTA *ptalft, *ptarft;
653  h = pixGetHeight(dew->pixs);
654  pta1 = ptaCreate(h);
655  pta2 = ptaCreate(h);
656  for (i = 0; i < h; i++) {
657  applyQuadraticFit(cl2, cl1, cl0, i, &x);
658  ptaAddPt(pta1, x, i);
659  applyQuadraticFit(cr2, cr1, cr0, i, &x);
660  ptaAddPt(pta2, x, i);
661  }
662  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
663  pixDisplayPta(pix1, pix1, pta2);
664  pixRenderHorizEndPoints(pix1, ptal2, ptar2, 0xff000000);
665  pixDisplay(pix1, 600, 800);
666  pixWriteDebug("/tmp/lept/dewmod/0051.png", pix1, IFF_PNG);
667  pixDestroy(&pix1);
668 
669  pix1 = pixDisplayPta(NULL, dew->pixs, pta1);
670  pixDisplayPta(pix1, pix1, pta2);
671  ptalft = ptaTranspose(ptal3);
672  ptarft = ptaTranspose(ptar3);
673  pixRenderHorizEndPoints(pix1, ptalft, ptarft, 0x0000ff00);
674  pixDisplay(pix1, 800, 800);
675  pixWriteDebug("/tmp/lept/dewmod/0052.png", pix1, IFF_PNG);
676  convertFilesToPdf("/tmp/lept/dewmod", "005", 135, 1.0, 0, 0,
677  "Dewarp Horiz Disparity",
678  "/tmp/lept/dewarp/horiz_disparity.pdf");
679  fprintf(stderr, "pdf file: /tmp/lept/dewarp/horiz_disparity.pdf\n");
680  pixDestroy(&pix1);
681  ptaDestroy(&pta1);
682  ptaDestroy(&pta2);
683  ptaDestroy(&ptalft);
684  ptaDestroy(&ptarft);
685  }
686 
687  /* Find the x value at the midpoints (in y) of the two vertical lines,
688  * ptal3 and ptar3. These are the reference values for each of the
689  * lines. Then use the difference between the these midpoint
690  * values and the actual x coordinates of the lines to represent
691  * the horizontal disparity (nald, nard) on the vertical lines
692  * for the sampled y values. */
693  ptaGetPt(ptal3, ny / 2, &refl, NULL);
694  ptaGetPt(ptar3, ny / 2, &refr, NULL);
695  nald = numaCreate(ny);
696  nard = numaCreate(ny);
697  for (i = 0; i < ny; i++) {
698  ptaGetPt(ptal3, i, &x, NULL);
699  numaAddNumber(nald, refl - x);
700  ptaGetPt(ptar3, i, &x, NULL);
701  numaAddNumber(nard, refr - x);
702  }
703 
704  /* Now for each pair of sampled values of the two lines (at the
705  * same value of y), do a linear interpolation to generate
706  * the horizontal disparity on all sampled points between them. */
707  ptaah = ptaaCreate(ny);
708  for (i = 0; i < ny; i++) {
709  pta = ptaCreate(2);
710  numaGetFValue(nald, i, &val);
711  ptaAddPt(pta, refl, val);
712  numaGetFValue(nard, i, &val);
713  ptaAddPt(pta, refr, val);
714  ptaGetLinearLSF(pta, &c1, &c0, NULL); /* horiz disparity along line */
715  ptat = ptaCreate(nx);
716  for (j = 0; j < nx; j++) {
717  x = j * sampling;
718  applyLinearFit(c1, c0, x, &val);
719  ptaAddPt(ptat, x, val);
720  }
721  ptaaAddPta(ptaah, ptat, L_INSERT);
722  ptaDestroy(&pta);
723  }
724  numaDestroy(&nald);
725  numaDestroy(&nard);
726 
727  /* Save the result in a fpix at the specified subsampling */
728  fpix = fpixCreate(nx, ny);
729  for (i = 0; i < ny; i++) {
730  for (j = 0; j < nx; j++) {
731  ptaaGetPt(ptaah, i, j, NULL, &val);
732  fpixSetPixel(fpix, j, i, val);
733  }
734  }
735  dew->samphdispar = fpix;
736  dew->hsuccess = 1;
737  ptaDestroy(&ptal2);
738  ptaDestroy(&ptar2);
739  ptaDestroy(&ptal3);
740  ptaDestroy(&ptar3);
741  ptaaDestroy(&ptaah);
742  return 0;
743 }
744 
745 
761 PTAA *
763  l_int32 debugflag)
764 {
765 char buf[64];
766 l_int32 i, w, h, bx, by, nsegs, csize1, csize2;
767 BOXA *boxa;
768 PIX *pix1, *pix2;
769 PIXA *pixa1, *pixa2;
770 PTA *pta;
771 PTAA *ptaa;
772 
773  PROCNAME("dewarpGetTextlineCenters");
774 
775  if (!pixs || pixGetDepth(pixs) != 1)
776  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
777  pixGetDimensions(pixs, &w, &h, NULL);
778 
779  if (debugflag) L_INFO("finding text line centers\n", procName);
780 
781  /* Filter to solidify the text lines within the x-height region,
782  * and to remove most of the ascenders and descenders.
783  * We start with a small vertical opening to remove noise beyond
784  * the line that can cause an error in the line end points.
785  * The small closing (csize1) is used to bridge the gaps between
786  * letters. The large closing (csize2) bridges the gaps between
787  * words; using 1/30 of the page width usually suffices. */
788  csize1 = L_MAX(15, w / 80);
789  csize2 = L_MAX(40, w / 30);
790  snprintf(buf, sizeof(buf), "o1.3 + c%d.1 + o%d.1 + c%d.1",
791  csize1, csize1, csize2);
792  pix1 = pixMorphSequence(pixs, buf, 0);
793 
794  /* Remove the components (e.g., embedded images) that have
795  * long vertical runs (>= 50 pixels). You can't use bounding
796  * boxes because connected component b.b. of lines can be quite
797  * tall due to slope and curvature. */
798  pix2 = pixMorphSequence(pix1, "e1.50", 0); /* seed */
799  pixSeedfillBinary(pix2, pix2, pix1, 8); /* tall components */
800  pixXor(pix2, pix2, pix1); /* remove tall */
801 
802  if (debugflag) {
803  lept_mkdir("lept/dewmod");
804  pixWriteDebug("/tmp/lept/dewmod/0011.tif", pix1, IFF_TIFF_G4);
805  pixDisplayWithTitle(pix1, 0, 600, "pix1", 1);
806  pixWriteDebug("/tmp/lept/dewmod/0012.tif", pix2, IFF_TIFF_G4);
807  pixDisplayWithTitle(pix2, 0, 800, "pix2", 1);
808  }
809  pixDestroy(&pix1);
810 
811  /* Get the 8-connected components ... */
812  boxa = pixConnComp(pix2, &pixa1, 8);
813  pixDestroy(&pix2);
814  boxaDestroy(&boxa);
815  if (pixaGetCount(pixa1) == 0) {
816  pixaDestroy(&pixa1);
817  return NULL;
818  }
819 
820  /* ... and remove the short width and very short height c.c */
821  pixa2 = pixaSelectBySize(pixa1, 100, 4, L_SELECT_IF_BOTH,
822  L_SELECT_IF_GT, NULL);
823  if ((nsegs = pixaGetCount(pixa2)) == 0) {
824  pixaDestroy(&pixa1);
825  pixaDestroy(&pixa2);
826  return NULL;
827  }
828  if (debugflag) {
829  pix2 = pixaDisplay(pixa2, w, h);
830  pixWriteDebug("/tmp/lept/dewmod/0013.tif", pix2, IFF_TIFF_G4);
831  pixDisplayWithTitle(pix2, 0, 1000, "pix2", 1);
832  pixDestroy(&pix2);
833  }
834 
835  /* For each c.c., get the weighted center of each vertical column.
836  * The result is a set of points going approximately through
837  * the center of the x-height part of the text line. */
838  ptaa = ptaaCreate(nsegs);
839  for (i = 0; i < nsegs; i++) {
840  pixaGetBoxGeometry(pixa2, i, &bx, &by, NULL, NULL);
841  pix2 = pixaGetPix(pixa2, i, L_CLONE);
842  pta = dewarpGetMeanVerticals(pix2, bx, by);
843  ptaaAddPta(ptaa, pta, L_INSERT);
844  pixDestroy(&pix2);
845  }
846  if (debugflag) {
847  pix1 = pixCreateTemplate(pixs);
848  pix2 = pixDisplayPtaa(pix1, ptaa);
849  pixWriteDebug("/tmp/lept/dewmod/0014.tif", pix2, IFF_PNG);
850  pixDisplayWithTitle(pix2, 0, 1200, "pix3", 1);
851  pixDestroy(&pix1);
852  pixDestroy(&pix2);
853  }
854 
855  pixaDestroy(&pixa1);
856  pixaDestroy(&pixa2);
857  return ptaa;
858 }
859 
860 
869 static PTA *
871  l_int32 x,
872  l_int32 y)
873 {
874 l_int32 w, h, i, j, wpl, sum, count;
875 l_uint32 *line, *data;
876 PTA *pta;
877 
878  PROCNAME("pixGetMeanVerticals");
879 
880  if (!pixs || pixGetDepth(pixs) != 1)
881  return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
882 
883  pixGetDimensions(pixs, &w, &h, NULL);
884  pta = ptaCreate(w);
885  data = pixGetData(pixs);
886  wpl = pixGetWpl(pixs);
887  for (j = 0; j < w; j++) {
888  line = data;
889  sum = count = 0;
890  for (i = 0; i < h; i++) {
891  if (GET_DATA_BIT(line, j) == 1) {
892  sum += i;
893  count += 1;
894  }
895  line += wpl;
896  }
897  if (count == 0) continue;
898  ptaAddPt(pta, x + j, y + (sum / count));
899  }
900 
901  return pta;
902 }
903 
904 
915 PTAA *
917  PTAA *ptaas,
918  l_float32 fract,
919  l_int32 debugflag)
920 {
921 l_int32 w, n, i, index, maxlen, len;
922 l_float32 minx, maxx;
923 NUMA *na, *naindex;
924 PIX *pix1, *pix2;
925 PTA *pta;
926 PTAA *ptaad;
927 
928  PROCNAME("dewarpRemoveShortLines");
929 
930  if (!pixs || pixGetDepth(pixs) != 1)
931  return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
932  if (!ptaas)
933  return (PTAA *)ERROR_PTR("ptaas undefined", procName, NULL);
934 
935  pixGetDimensions(pixs, &w, NULL, NULL);
936  n = ptaaGetCount(ptaas);
937  ptaad = ptaaCreate(n);
938  na = numaCreate(n);
939  for (i = 0; i < n; i++) {
940  pta = ptaaGetPta(ptaas, i, L_CLONE);
941  ptaGetRange(pta, &minx, &maxx, NULL, NULL);
942  numaAddNumber(na, maxx - minx + 1);
943  ptaDestroy(&pta);
944  }
945 
946  /* Sort by length and find all that are long enough */
947  naindex = numaGetSortIndex(na, L_SORT_DECREASING);
948  numaGetIValue(naindex, 0, &index);
949  numaGetIValue(na, index, &maxlen);
950  if (maxlen < 0.5 * w)
951  L_WARNING("lines are relatively short\n", procName);
952  pta = ptaaGetPta(ptaas, index, L_CLONE);
953  ptaaAddPta(ptaad, pta, L_INSERT);
954  for (i = 1; i < n; i++) {
955  numaGetIValue(naindex, i, &index);
956  numaGetIValue(na, index, &len);
957  if (len < fract * maxlen) break;
958  pta = ptaaGetPta(ptaas, index, L_CLONE);
959  ptaaAddPta(ptaad, pta, L_INSERT);
960  }
961 
962  if (debugflag) {
963  pix1 = pixCopy(NULL, pixs);
964  pix2 = pixDisplayPtaa(pix1, ptaad);
965  pixDisplayWithTitle(pix2, 0, 200, "pix4", 1);
966  pixDestroy(&pix1);
967  pixDestroy(&pix2);
968  }
969 
970  numaDestroy(&na);
971  numaDestroy(&naindex);
972  return ptaad;
973 }
974 
975 
998 static l_int32
1000  PTAA *ptaa,
1001  PTA **pptal,
1002  PTA **pptar)
1003 {
1004 l_int32 i, n, npt, x, y;
1005 l_float32 miny, maxy, ratio;
1006 PTA *pta, *ptal1, *ptar1;
1007 
1008  PROCNAME("dewarpGetLineEndPoints");
1009 
1010  if (!pptal || !pptar)
1011  return ERROR_INT("&ptal and &ptar not both defined", procName, 1);
1012  *pptal = *pptar = NULL;
1013  if (!ptaa)
1014  return ERROR_INT("ptaa undefined", procName, 1);
1015 
1016  /* Are there at least 10 lines? */
1017  n = ptaaGetCount(ptaa);
1018  if (n < L_MIN_LINES_FOR_HORIZ_1) {
1019  L_INFO("only %d lines; too few\n", procName, n);
1020  return 1;
1021  }
1022 
1023  /* Extract the line end points, and transpose x and y values */
1024  ptal1 = ptaCreate(n);
1025  ptar1 = ptaCreate(n);
1026  for (i = 0; i < n; i++) {
1027  pta = ptaaGetPta(ptaa, i, L_CLONE);
1028  ptaGetIPt(pta, 0, &x, &y);
1029  ptaAddPt(ptal1, y, x); /* transpose */
1030  npt = ptaGetCount(pta);
1031  ptaGetIPt(pta, npt - 1, &x, &y);
1032  ptaAddPt(ptar1, y, x); /* transpose */
1033  ptaDestroy(&pta);
1034  }
1035 
1036  /* Use the min and max of the y value on the left side. */
1037  ptaGetRange(ptal1, &miny, &maxy, NULL, NULL);
1038  ratio = (maxy - miny) / (l_float32)h;
1039  if (ratio < L_MIN_RATIO_LINES_TO_HEIGHT) {
1040  L_INFO("ratio lines to height, %f, too small\n", procName, ratio);
1041  ptaDestroy(&ptal1);
1042  ptaDestroy(&ptar1);
1043  return 1;
1044  }
1045 
1046  /* Sort from top to bottom */
1047  *pptal = ptaSort(ptal1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1048  *pptar = ptaSort(ptar1, L_SORT_BY_X, L_SORT_INCREASING, NULL);
1049  ptaDestroy(&ptal1);
1050  ptaDestroy(&ptar1);
1051  return 0;
1052 }
1053 
1054 
1077 static l_int32
1079  PTA *ptal,
1080  PTA *ptar,
1081  PTA **pptalf,
1082  PTA **pptarf)
1083 {
1084 l_int32 w, i, n;
1085 l_float32 ymin, ymax, xvall, xvalr, yvall, yvalr;
1086 PTA *ptal1, *ptar1, *ptal2, *ptar2;
1087 
1088  PROCNAME("dewarpFilterLineEndPoints");
1089  if (!ptal || !ptar)
1090  return ERROR_INT("ptal or ptar not defined", procName, 1);
1091  *pptalf = *pptarf = NULL;
1092 
1093  /* First filter for lines near left and right margins */
1094  w = pixGetWidth(dew->pixs);
1095  ptaGetMinMax(ptal, NULL, &ymin, NULL, NULL);
1096  ptaGetMinMax(ptar, NULL, NULL, NULL, &ymax);
1097  n = ptaGetCount(ptal); /* ptar is the same size; at least 10 */
1098  ptal1 = ptaCreate(n);
1099  ptar1 = ptaCreate(n);
1100  for (i = 0; i < n; i++) {
1101  ptaGetPt(ptal, i, &xvall, &yvall);
1102  ptaGetPt(ptar, i, &xvalr, &yvalr);
1103  if (yvall < ymin + 0.20 * (w - ymin) &&
1104  yvalr > 0.80 * ymax) {
1105  ptaAddPt(ptal1, xvall, yvall);
1106  ptaAddPt(ptar1, xvalr, yvalr);
1107  }
1108  }
1109  if (dew->debug) {
1110  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left2.pta", ptal1, 1);
1111  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right2.pta", ptar1, 1);
1112  }
1113 
1114  n = L_MIN(ptaGetCount(ptal1), ptaGetCount(ptar1));
1115  if (n < L_MIN_LINES_FOR_HORIZ_1 - 2) {
1116  ptaDestroy(&ptal1);
1117  ptaDestroy(&ptar1);
1118  L_INFO("First filter: only %d endpoints; needed 8\n", procName, n);
1119  return 1;
1120  }
1121 
1122  /* Remove outlier points */
1123  ptal2 = dewarpRemoveBadEndPoints(w, ptal1);
1124  ptar2 = dewarpRemoveBadEndPoints(w, ptar1);
1125  ptaDestroy(&ptal1);
1126  ptaDestroy(&ptar1);
1127  if (!ptal2 || !ptar2) {
1128  ptaDestroy(&ptal2);
1129  ptaDestroy(&ptar2);
1130  L_INFO("Second filter: too few endpoints left after outliers removed\n",
1131  procName);
1132  return 1;
1133  }
1134  if (dew->debug) {
1135  ptaWriteDebug("/tmp/lept/dewdebug/endpts_left3.pta", ptal2, 1);
1136  ptaWriteDebug("/tmp/lept/dewdebug/endpts_right3.pta", ptar2, 1);
1137  }
1138 
1139  *pptalf = ptal2;
1140  *pptarf = ptar2;
1141  return 0;
1142 }
1143 
1144 
1163 static PTA *
1165  PTA *ptas)
1166 {
1167 l_int32 i, n, nu, nd;
1168 l_float32 rval, xval, yval, delta;
1169 PTA *ptau1, *ptau2, *ptad1, *ptad2;
1170 
1171  PROCNAME("dewarpRemoveBadEndPoints");
1172 
1173  if (!ptas)
1174  return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
1175 
1176  delta = w * L_ALLOWED_W_FRACT;
1177  n = ptaGetCount(ptas); /* will be at least 8 */
1178 
1179  /* Check the upper half */
1180  ptau1 = ptaSelectRange(ptas, 0, n / 2);
1181  ptaGetRankValue(ptau1, 0.5, NULL, L_SORT_BY_Y, &rval);
1182  nu = ptaGetCount(ptau1);
1183  ptau2 = ptaCreate(nu);
1184  for (i = 0; i < nu; i++) {
1185  ptaGetPt(ptau1, i, &xval, &yval); /* transposed */
1186  if (L_ABS(rval - yval) <= delta)
1187  ptaAddPt(ptau2, xval, yval);
1188  }
1189  ptaDestroy(&ptau1);
1190  if (ptaGetCount(ptau2) < L_MIN_LINES_FOR_HORIZ_2) {
1191  ptaDestroy(&ptau2);
1192  L_INFO("Second filter: upper set is too small after outliers removed\n",
1193  procName);
1194  return NULL;
1195  }
1196 
1197  /* Check the lower half */
1198  ptad1 = ptaSelectRange(ptas, n / 2 + 1, -1);
1199  ptaGetRankValue(ptad1, 0.5, NULL, L_SORT_BY_Y, &rval);
1200  nd = ptaGetCount(ptad1);
1201  ptad2 = ptaCreate(nd);
1202  for (i = 0; i < nd; i++) {
1203  ptaGetPt(ptad1, i, &xval, &yval); /* transposed */
1204  if (L_ABS(rval - yval) <= delta)
1205  ptaAddPt(ptad2, xval, yval);
1206  }
1207  ptaDestroy(&ptad1);
1208  if (ptaGetCount(ptad2) < L_MIN_LINES_FOR_HORIZ_2) {
1209  ptaDestroy(&ptau2);
1210  ptaDestroy(&ptad2);
1211  L_INFO("Second filter: lower set is too small after outliers removed\n",
1212  procName);
1213  return NULL;
1214  }
1215 
1216  ptaJoin(ptau2, ptad2, 0, -1);
1217  ptaDestroy(&ptad2);
1218  return ptau2;
1219 }
1220 
1221 
1239 static l_int32
1241  l_int32 h,
1242  l_int32 *ptopline,
1243  l_int32 *pbotline)
1244 {
1245 l_int32 i, n, both_halves;
1246 l_float32 top, bot, y, fraction;
1247 
1248  PROCNAME("dewarpIsLineCoverageValid");
1249 
1250  if (!ptaa)
1251  return ERROR_INT("ptaa not defined", procName, 0);
1252  if ((n = ptaaGetCount(ptaa)) == 0)
1253  return ERROR_INT("ptaa empty", procName, 0);
1254  if (h <= 0)
1255  return ERROR_INT("invalid h", procName, 0);
1256  if (!ptopline || !pbotline)
1257  return ERROR_INT("&topline and &botline not defined", procName, 0);
1258 
1259  top = 100000.0;
1260  bot = 0.0;
1261  for (i = 0; i < n; i++) {
1262  ptaaGetPt(ptaa, i, 0, NULL, &y);
1263  if (y < top) top = y;
1264  if (y > bot) bot = y;
1265  }
1266  *ptopline = (l_int32)top;
1267  *pbotline = (l_int32)bot;
1268  both_halves = top < 0.5 * h && bot > 0.5 * h;
1269  fraction = (bot - top) / h;
1270  if (both_halves && fraction > 0.40)
1271  return 1;
1272  return 0;
1273 }
1274 
1275 
1295 static l_int32
1297  l_float32 *pa,
1298  l_float32 *pb,
1299  l_float32 *pc,
1300  l_float32 *pmederr)
1301 {
1302 l_int32 i, n;
1303 l_float32 x, y, xp, c0, c1, c2;
1304 NUMA *naerr;
1305 
1306  PROCNAME("dewarpQuadraticLSF");
1307 
1308  if (pmederr) *pmederr = 0.0;
1309  if (!pa || !pb || !pc)
1310  return ERROR_INT("not all ptrs are defined", procName, 1);
1311  *pa = *pb = *pc = 0.0;
1312  if (!ptad)
1313  return ERROR_INT("ptad not defined", procName, 1);
1314 
1315  /* Fit to the longest lines */
1316  ptaGetQuadraticLSF(ptad, &c2, &c1, &c0, NULL);
1317  *pa = c2;
1318  *pb = c1;
1319  *pc = c0;
1320 
1321  /* Optionally, find the median error */
1322  if (pmederr) {
1323  n = ptaGetCount(ptad);
1324  naerr = numaCreate(n);
1325  for (i = 0; i < n; i++) {
1326  ptaGetPt(ptad, i, &y, &xp);
1327  applyQuadraticFit(c2, c1, c0, y, &x);
1328  numaAddNumber(naerr, L_ABS(x - xp));
1329  }
1330  numaGetMedian(naerr, pmederr);
1331  numaDestroy(&naerr);
1332  }
1333  return 0;
1334 }
1335 
1336 /*----------------------------------------------------------------------*
1337  * Build disparity model for slope near binding *
1338  *----------------------------------------------------------------------*/
1377 l_ok
1379  PIX *pixb,
1380  l_float32 fractthresh,
1381  l_int32 parity)
1382 {
1383 l_int32 i, j, x, n1, n2, nb, ne, count, w, h, ival, prev;
1384 l_int32 istart, iend, first, last, x0, x1, nx, ny;
1385 l_float32 fract, delta, sum, aveval, fval, del, denom;
1386 l_float32 ca, cb, cc, cd, ce, y;
1387 BOX *box;
1388 BOXA *boxa1, *boxa2;
1389 NUMA *na1, *na2, *na3, *na4, *nasum;
1390 PIX *pix1;
1391 PTA *pta1;
1392 FPIX *fpix;
1393 
1394  PROCNAME("dewarpFindHorizSlopeDisparity");
1395 
1396  if (!dew)
1397  return ERROR_INT("dew not defined", procName, 1);
1398  if (!dew->vvalid || !dew->hvalid)
1399  return ERROR_INT("invalid vert or horiz disparity model", procName, 1);
1400  if (!pixb || pixGetDepth(pixb) != 1)
1401  return ERROR_INT("pixb not defined or not 1 bpp", procName, 1);
1402 
1403  if (dew->debug) L_INFO("finding slope horizontal disparity\n", procName);
1404 
1405  /* Find the bounding boxes of the vertical strokes; remove noise */
1406  pix1 = pixMorphSequence(pixb, "o1.10", 0);
1407  pixDisplay(pix1, 100, 100);
1408  boxa1 = pixConnCompBB(pix1, 4);
1409  boxa2 = boxaSelectBySize(boxa1, 0, 5, L_SELECT_HEIGHT, L_SELECT_IF_GT,
1410  NULL);
1411  nb = boxaGetCount(boxa2);
1412  fprintf(stderr, "number of components: %d\n", nb);
1413  boxaDestroy(&boxa1);
1414 
1415  /* Estimate the horizontal density of vertical strokes */
1416  na1 = numaCreate(0);
1417  numaSetParameters(na1, 0, 25);
1418  pixGetDimensions(pixb, &w, &h, NULL);
1419  for (x = 0; x + 50 < w; x += 25) {
1420  box = boxCreate(x, 0, 50, h);
1421  boxaContainedInBoxCount(boxa2, box, &count);
1422  numaAddNumber(na1, count);
1423  boxDestroy(&box);
1424  }
1425  if (dew->debug) {
1426  lept_mkdir("lept/dew");
1427  gplotSimple1(na1, GPLOT_PNG, "/tmp/lept/dew/0091", NULL);
1428  lept_mv("/tmp/lept/dew/0091.png", "lept/dewmod", NULL, NULL);
1429  pixWriteDebug("/tmp/lept/dewmod/0090.png", pix1, IFF_PNG);
1430  }
1431  pixDestroy(&pix1);
1432  boxaDestroy(&boxa2);
1433 
1434  /* Find the left and right end local maxima; if the difference
1435  * is small, quit. */
1436  n1 = numaGetCount(na1);
1437  prev = 0;
1438  istart = 0;
1439  first = 0;
1440  for (i = 0; i < n1; i++) {
1441  numaGetIValue(na1, i, &ival);
1442  if (ival >= prev) {
1443  prev = ival;
1444  continue;
1445  } else {
1446  first = prev;
1447  istart = i - 1;
1448  break;
1449  }
1450  }
1451  prev = 0;
1452  last = 0;
1453  iend = n1 - 1;
1454  for (i = n1 - 1; i >= 0; i--) {
1455  numaGetIValue(na1, i, &ival);
1456  if (ival >= prev) {
1457  prev = ival;
1458  continue;
1459  } else {
1460  last = prev;
1461  iend = i + 1;
1462  break;
1463  }
1464  }
1465  na2 = numaClipToInterval(na1, istart, iend);
1466  numaDestroy(&na1);
1467  n2 = numaGetCount(na2);
1468  delta = (parity == 0) ? last - first : first - last;
1469  denom = L_MAX(1.0, (l_float32)(L_MIN(first, last)));
1470  fract = (l_float32)delta / denom;
1471  if (dew->debug) {
1472  L_INFO("Slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1473  procName, first, last, fract);
1474  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0092", NULL);
1475  lept_mv("/tmp/lept/dew/0092.png", "lept/dewmod", NULL, NULL);
1476  }
1477  if (fract < fractthresh) {
1478  L_INFO("Small slope-disparity: first = %d, last = %d, fract = %7.3f\n",
1479  procName, first, last, fract);
1480  numaDestroy(&na2);
1481  return 0;
1482  }
1483 
1484  /* Find the density far from the binding, and normalize to 1. */
1485  ne = n2 - n2 % 2;
1486  if (parity == 0)
1487  numaGetSumOnInterval(na2, 0, ne / 2 - 1, &sum);
1488  else /* parity == 1 */
1489  numaGetSumOnInterval(na2, ne / 2, ne - 1, &sum);
1490  denom = L_MAX(1.0, (l_float32)(ne / 2));
1491  aveval = sum / denom;
1492  na3 = numaMakeConstant(aveval, n2);
1493  numaArithOp(na2, na2, na3, L_ARITH_DIVIDE);
1494  numaDestroy(&na3);
1495  if (dew->debug) {
1496  L_INFO("Average background density: %5.1f\n", procName, aveval);
1497  gplotSimple1(na2, GPLOT_PNG, "/tmp/lept/dew/0093", NULL);
1498  lept_mv("/tmp/lept/dew/0093.png", "lept/dewmod", NULL, NULL);
1499  }
1500 
1501  /* Fit the normalized density curve to a quartic */
1502  pta1 = numaConvertToPta1(na2);
1503  ptaWriteStream(stderr, pta1, 0);
1504 /* ptaGetQuadraticLSF(pta1, NULL, NULL, NULL, &na3); */
1505  ptaGetQuarticLSF(pta1, &ca, &cb, &cc, &cd, &ce, &na3);
1506  ptaGetArrays(pta1, &na4, NULL);
1507  if (dew->debug) {
1508  gplotSimpleXY1(na4, na3, GPLOT_LINES, GPLOT_PNG,
1509  "/tmp/lept/dew/0094", NULL);
1510  lept_mv("/tmp/lept/dew/0094.png", "lept/dewmod", NULL, NULL);
1511  }
1512  ptaDestroy(&pta1);
1513 
1514  /* Integrate from the high point down to 1 (or v.v) to get the
1515  * disparity needed to make the density constant. */
1516  nasum = numaMakeConstant(0, w); /* area under the curve above 1.0 */
1517  if (parity == 0) {
1518  for (i = n2 - 1; i >= 0; i--) {
1519  numaGetFValue(na3, i, &fval);
1520  if (fval < 1.0) break;
1521  }
1522  numaGetIValue(na4, i + 1, &x0);
1523  numaGetIValue(na4, n2 - 1, &x1);
1524  numaSetParameters(nasum, x0, 1);
1525  sum = 0.0;
1526  for (x = x0; x < x1; x++) {
1527  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1528  sum += (y - 1.0);
1529  numaReplaceNumber(nasum, x, sum);
1530  }
1531  for (x = x1; x < w; x++)
1532  numaReplaceNumber(nasum, x, sum);
1533  } else { /* parity == 1 */
1534  for (i = 0; i < n2; i++) {
1535  numaGetFValue(na3, i, &fval);
1536  if (fval < 1.0) break;
1537  }
1538  numaGetIValue(na4, 0, &x0);
1539  numaGetIValue(na4, i - 1, &x1);
1540  numaSetParameters(nasum, x0, 1);
1541  sum = 0.0;
1542  for (x = x1; x >= x0; x--) {
1543  applyQuarticFit(ca, cb, cc, cd, ce, (l_float32)x, &y);
1544  sum += (y - 1.0);
1545  numaReplaceNumber(nasum, x, sum);
1546  }
1547  for (x = x0; x >= 0; x--)
1548  numaReplaceNumber(nasum, x, sum);
1549  }
1550 
1551  /* Save the result in a fpix at the specified subsampling */
1552  nx = dew->nx;
1553  ny = dew->ny;
1554  fpix = fpixCreate(nx, ny);
1555  del = (l_float32)w / (l_float32)nx;
1556  for (i = 0; i < ny; i++) {
1557  for (j = 0; j < nx; j++) {
1558  x = del * j;
1559  numaGetFValue(nasum, x, &fval);
1560  fpixSetPixel(fpix, j, i, fval);
1561  }
1562  }
1563  dew->sampydispar = fpix;
1564  dew->ysuccess = 1;
1565 
1566  numaDestroy(&na2);
1567  numaDestroy(&na3);
1568  numaDestroy(&na4);
1569  numaDestroy(&nasum);
1570  return 0;
1571 }
1572 
1573 
1574 /*----------------------------------------------------------------------*
1575  * Build line disparity model *
1576  *----------------------------------------------------------------------*/
1605 l_ok
1607  l_int32 opensize,
1608  const char *debugfile)
1609 {
1610 char buf[64];
1611 l_int32 i, j, bx, by, ret, nlines;
1612 BOXA *boxa;
1613 PIX *pixs, *pixh, *pixv, *pix, *pix1, *pix2;
1614 PIXA *pixa1, *pixa2;
1615 PTA *pta;
1616 PTAA *ptaa1, *ptaa2;
1617 
1618  PROCNAME("dewarpBuildLineModel");
1619 
1620  if (!dew)
1621  return ERROR_INT("dew not defined", procName, 1);
1622  if (opensize < 3) {
1623  L_WARNING("opensize should be >= 3; setting to 8\n", procName);
1624  opensize = 8; /* default */
1625  }
1626 
1627  dew->debug = (debugfile) ? 1 : 0;
1628  dew->vsuccess = dew->hsuccess = 0;
1629  pixs = dew->pixs;
1630  if (debugfile) {
1631  lept_rmdir("lept/dewline"); /* erase previous images */
1632  lept_mkdir("lept/dewline");
1633  lept_rmdir("lept/dewmod"); /* erase previous images */
1634  lept_mkdir("lept/dewmod");
1635  lept_mkdir("lept/dewarp");
1636  pixDisplayWithTitle(pixs, 0, 0, "pixs", 1);
1637  pixWriteDebug("/tmp/lept/dewline/001.png", pixs, IFF_PNG);
1638  }
1639 
1640  /* Extract and solidify the horizontal and vertical lines. We use
1641  * the horizontal lines to derive the vertical disparity, and v.v.
1642  * Both disparities are computed using the vertical disparity
1643  * algorithm; the horizontal disparity is found from the
1644  * vertical lines by rotating them clockwise by 90 degrees.
1645  * On the first pass, we compute the horizontal disparity, from
1646  * the vertical lines, by rotating them by 90 degrees (so they
1647  * are horizontal) and computing the vertical disparity on them;
1648  * we rotate the resulting fpix array for the horizontal disparity
1649  * back by -90 degrees. On the second pass, we compute the vertical
1650  * disparity from the horizontal lines in the usual fashion. */
1651  snprintf(buf, sizeof(buf), "d1.3 + c%d.1 + o%d.1", opensize - 2, opensize);
1652  pixh = pixMorphSequence(pixs, buf, 0); /* horiz */
1653  snprintf(buf, sizeof(buf), "d3.1 + c1.%d + o1.%d", opensize - 2, opensize);
1654  pix1 = pixMorphSequence(pixs, buf, 0); /* vert */
1655  pixv = pixRotateOrth(pix1, 1); /* vert rotated to horizontal */
1656  pixa1 = pixaCreate(2);
1657  pixaAddPix(pixa1, pixv, L_INSERT); /* get horizontal disparity first */
1658  pixaAddPix(pixa1, pixh, L_INSERT);
1659  pixDestroy(&pix1);
1660 
1661  /*--------------------------------------------------------------*/
1662  /* Process twice: first for horiz disparity, then for vert */
1663  /*--------------------------------------------------------------*/
1664  for (i = 0; i < 2; i++) {
1665  pix = pixaGetPix(pixa1, i, L_CLONE);
1666  pixDisplay(pix, 0, 900);
1667  boxa = pixConnComp(pix, &pixa2, 8);
1668  nlines = boxaGetCount(boxa);
1669  boxaDestroy(&boxa);
1670  if (nlines < dew->minlines) {
1671  L_WARNING("only found %d lines\n", procName, nlines);
1672  pixDestroy(&pix);
1673  pixaDestroy(&pixa1);
1674  continue;
1675  }
1676 
1677  /* Identify the pixels along the skeleton of each line */
1678  ptaa1 = ptaaCreate(nlines);
1679  for (j = 0; j < nlines; j++) {
1680  pixaGetBoxGeometry(pixa2, j, &bx, &by, NULL, NULL);
1681  pix1 = pixaGetPix(pixa2, j, L_CLONE);
1682  pta = dewarpGetMeanVerticals(pix1, bx, by);
1683  ptaaAddPta(ptaa1, pta, L_INSERT);
1684  pixDestroy(&pix1);
1685  }
1686  pixaDestroy(&pixa2);
1687  if (debugfile) {
1688  pix1 = pixConvertTo32(pix);
1689  pix2 = pixDisplayPtaa(pix1, ptaa1);
1690  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 2 + 2 * i);
1691  pixWriteDebug(buf, pix2, IFF_PNG);
1692  pixDestroy(&pix1);
1693  pixDestroy(&pix2);
1694  }
1695 
1696  /* Remove all lines that are not at least 0.75 times the length
1697  * of the longest line. */
1698  ptaa2 = dewarpRemoveShortLines(pix, ptaa1, 0.75, DEBUG_SHORT_LINES);
1699  if (debugfile) {
1700  pix1 = pixConvertTo32(pix);
1701  pix2 = pixDisplayPtaa(pix1, ptaa2);
1702  snprintf(buf, sizeof(buf), "/tmp/lept/dewline/%03d.png", 3 + 2 * i);
1703  pixWriteDebug(buf, pix2, IFF_PNG);
1704  pixDestroy(&pix1);
1705  pixDestroy(&pix2);
1706  }
1707  ptaaDestroy(&ptaa1);
1708  nlines = ptaaGetCount(ptaa2);
1709  if (nlines < dew->minlines) {
1710  pixDestroy(&pix);
1711  ptaaDestroy(&ptaa2);
1712  L_WARNING("%d lines: too few to build model\n", procName, nlines);
1713  continue;
1714  }
1715 
1716  /* Get the sampled 'vertical' disparity from the textline
1717  * centers. The disparity array will push pixels vertically
1718  * so that each line is flat and centered at the y-position
1719  * of the mid-point. */
1720  ret = dewarpFindVertDisparity(dew, ptaa2, 1 - i);
1721 
1722  /* If i == 0, move the result to the horizontal disparity,
1723  * rotating it back by -90 degrees. */
1724  if (i == 0) { /* horizontal disparity, really */
1725  if (ret) {
1726  L_WARNING("horizontal disparity not built\n", procName);
1727  } else {
1728  L_INFO("hsuccess = 1\n", procName);
1729  dew->samphdispar = fpixRotateOrth(dew->sampvdispar, 3);
1730  fpixDestroy(&dew->sampvdispar);
1731  if (debugfile)
1732  lept_mv("/tmp/lept/dewarp/vert_disparity.pdf",
1733  "lept/dewarp", "horiz_disparity.pdf", NULL);
1734  }
1735  dew->hsuccess = dew->vsuccess;
1736  dew->vsuccess = 0;
1737  } else { /* i == 1 */
1738  if (ret)
1739  L_WARNING("vertical disparity not built\n", procName);
1740  else
1741  L_INFO("vsuccess = 1\n", procName);
1742  }
1743  ptaaDestroy(&ptaa2);
1744  pixDestroy(&pix);
1745  }
1746  pixaDestroy(&pixa1);
1747 
1748  /* Debug output */
1749  if (debugfile) {
1750  if (dew->vsuccess == 1) {
1751  dewarpPopulateFullRes(dew, NULL, 0, 0);
1752  pix1 = fpixRenderContours(dew->fullvdispar, 3.0, 0.15);
1753  pixWriteDebug("/tmp/lept/dewline/006.png", pix1, IFF_PNG);
1754  pixDisplay(pix1, 1000, 0);
1755  pixDestroy(&pix1);
1756  }
1757  if (dew->hsuccess == 1) {
1758  pix1 = fpixRenderContours(dew->fullhdispar, 3.0, 0.15);
1759  pixWriteDebug("/tmp/lept/dewline/007.png", pix1, IFF_PNG);
1760  pixDisplay(pix1, 1000, 0);
1761  pixDestroy(&pix1);
1762  }
1763  convertFilesToPdf("/tmp/lept/dewline", NULL, 135, 1.0, 0, 0,
1764  "Dewarp Build Line Model", debugfile);
1765  fprintf(stderr, "pdf file: %s\n", debugfile);
1766  }
1767 
1768  return 0;
1769 }
1770 
1771 
1772 /*----------------------------------------------------------------------*
1773  * Query model status *
1774  *----------------------------------------------------------------------*/
1789 l_ok
1791  l_int32 pageno,
1792  l_int32 *pvsuccess,
1793  l_int32 *phsuccess)
1794 {
1795 L_DEWARP *dew;
1796 
1797  PROCNAME("dewarpaModelStatus");
1798 
1799  if (pvsuccess) *pvsuccess = 0;
1800  if (phsuccess) *phsuccess = 0;
1801  if (!dewa)
1802  return ERROR_INT("dewa not defined", procName, 1);
1803 
1804  if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL)
1805  return ERROR_INT("dew not retrieved", procName, 1);
1806  if (pvsuccess) *pvsuccess = dew->vsuccess;
1807  if (phsuccess) *phsuccess = dew->hsuccess;
1808  return 0;
1809 }
1810 
1811 
1812 /*----------------------------------------------------------------------*
1813  * Rendering helpers *
1814  *----------------------------------------------------------------------*/
1823 static l_int32
1825  NUMA *namidys,
1826  l_int32 linew)
1827 {
1828 l_int32 i, n, w, yval, rval, gval, bval;
1829 PIXCMAP *cmap;
1830 
1831  PROCNAME("pixRenderMidYs");
1832 
1833  if (!pixs)
1834  return ERROR_INT("pixs not defined", procName, 1);
1835  if (!namidys)
1836  return ERROR_INT("namidys not defined", procName, 1);
1837 
1838  w = pixGetWidth(pixs);
1839  n = numaGetCount(namidys);
1840  cmap = pixcmapCreateRandom(8, 0, 0);
1841  for (i = 0; i < n; i++) {
1842  pixcmapGetColor(cmap, i % 256, &rval, &gval, &bval);
1843  numaGetIValue(namidys, i, &yval);
1844  pixRenderLineArb(pixs, 0, yval, w, yval, linew, rval, gval, bval);
1845  }
1846  pixcmapDestroy(&cmap);
1847  return 0;
1848 }
1849 
1850 
1860 static l_int32
1862  PTA *ptal,
1863  PTA *ptar,
1864  l_uint32 color)
1865 {
1866 PIX *pixcirc;
1867 PTA *ptalt, *ptart, *ptacirc;
1868 
1869  PROCNAME("pixRenderHorizEndPoints");
1870 
1871  if (!pixs)
1872  return ERROR_INT("pixs not defined", procName, 1);
1873  if (!ptal || !ptar)
1874  return ERROR_INT("ptal and ptar not both defined", procName, 1);
1875 
1876  ptacirc = generatePtaFilledCircle(5);
1877  pixcirc = pixGenerateFromPta(ptacirc, 11, 11);
1878  ptalt = ptaTranspose(ptal);
1879  ptart = ptaTranspose(ptar);
1880 
1881  pixDisplayPtaPattern(pixs, pixs, ptalt, pixcirc, 5, 5, color);
1882  pixDisplayPtaPattern(pixs, pixs, ptart, pixcirc, 5, 5, color);
1883  ptaDestroy(&ptacirc);
1884  ptaDestroy(&ptalt);
1885  ptaDestroy(&ptart);
1886  pixDestroy(&pixcirc);
1887  return 0;
1888 }
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:1766
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:1145
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:2298
static l_int32 dewarpIsLineCoverageValid(PTAA *ptaa2, l_int32 h, l_int32 *ptopline, l_int32 *pbotline)
dewarpIsLineCoverageValid()
Definition: dewarp2.c:1240
l_int32 debug
Definition: dewarp.h:187
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:692
static PTA * dewarpRemoveBadEndPoints(l_int32 w, PTA *ptas)
dewarpRemoveBadEndPoints()
Definition: dewarp2.c:1164
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:1125
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1944
l_ok ptaGetRankValue(PTA *pta, l_float32 fract, PTA *ptasort, l_int32 sorttype, l_float32 *pval)
ptaGetRankValue()
Definition: ptafunc2.c:264
Definition: pix.h:717
NUMA * numaGetSortIndex(NUMA *na, l_int32 sortorder)
numaGetSortIndex()
Definition: numafunc1.c:2637
l_int32 ny
Definition: dewarp.h:177
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3233
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:547
PIX * pixGenerateFromPta(PTA *pta, l_int32 w, l_int32 h)
pixGenerateFromPta()
Definition: ptafunc1.c:1961
NUMA * numaSortByIndex(NUMA *nas, NUMA *naindex)
numaSortByIndex()
Definition: numafunc1.c:2786
l_ok ptaWriteStream(FILE *fp, PTA *pta, l_int32 type)
ptaWriteStream()
Definition: ptabasic.c:839
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:342
l_ok numaWriteDebug(const char *filename, NUMA *na)
numaWriteDebug()
Definition: numabasic.c:1193
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:163
l_ok boxaContainedInBoxCount(BOXA *boxa, BOX *box, l_int32 *pcount)
boxaContainedInBoxCount()
Definition: boxfunc1.c:211
Definition: pix.h:716
PTAA * dewarpRemoveShortLines(PIX *pixs, PTAA *ptaas, l_float32 fract, l_int32 debugflag)
dewarpRemoveShortLines()
Definition: dewarp2.c:916
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:217
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:1388
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:116
l_ok numaGetMedian(NUMA *na, l_float32 *pval)
numaGetMedian()
Definition: numafunc1.c:3135
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:2430
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:781
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:870
l_ok ptaGetArrays(PTA *pta, NUMA **pnax, NUMA **pnay)
ptaGetArrays()
Definition: ptabasic.c:616
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:265
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:504
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:580
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PTA * ptaSort(PTA *ptas, l_int32 sorttype, l_int32 sortorder, NUMA **pnaindex)
ptaSort()
Definition: ptafunc2.c:96
l_int32 ysuccess
Definition: dewarp.h:182
l_ok dewarpBuildLineModel(L_DEWARP *dew, l_int32 opensize, const char *debugfile)
dewarpBuildLineModel()
Definition: dewarp2.c:1606
PIX * pixaDisplay(PIXA *pixa, l_int32 w, l_int32 h)
pixaDisplay()
Definition: pixafunc2.c:184
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1105
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:239
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
l_ok ptaGetLinearLSF(PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit)
ptaGetLinearLSF()
Definition: ptafunc1.c:1044
Definition: pix.h:492
l_int32 pageno
Definition: dewarp.h:165
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:164
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:683
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:1523
struct L_Dewarpa * dewa
Definition: dewarp.h:153
l_ok dewarpBuildPageModel(L_DEWARP *dew, const char *debugfile)
dewarpBuildPageModel()
Definition: dewarp2.c:148
PTAA * ptaaSortByIndex(PTAA *ptaas, NUMA *naindex)
ptaaSortByIndex()
Definition: ptafunc2.c:225
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:1715
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:147
l_ok dewarpFindHorizSlopeDisparity(L_DEWARP *dew, PIX *pixb, l_float32 fractthresh, l_int32 parity)
dewarpFindHorizSlopeDisparity()
Definition: dewarp2.c:1378
NUMA * numaArithOp(NUMA *nad, NUMA *na1, NUMA *na2, l_int32 op)
numaArithOp()
Definition: numafunc1.c:166
l_ok dewarpFindVertDisparity(L_DEWARP *dew, PTAA *ptaa, l_int32 rotflag)
dewarpFindVertDisparity()
Definition: dewarp2.c:294
PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag)
ptaaGetPta()
Definition: ptabasic.c:1094
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:727
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:503
PTA * numaConvertToPta1(NUMA *na)
numaConvertToPta1()
Definition: ptafunc1.c:2247
Definition: array.h:59
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
PIX * pixDisplayPtaa(PIX *pixs, PTAA *ptaa)
pixDisplayPtaa()
Definition: ptafunc1.c:2609
l_int32 minlines
Definition: dewarp.h:168
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:631
BOXA * pixConnCompBB(PIX *pixs, l_int32 connectivity)
pixConnCompBB()
Definition: conncomp.c:306
l_ok dewarpFindHorizDisparity(L_DEWARP *dew, PTAA *ptaa)
dewarpFindHorizDisparity()
Definition: dewarp2.c:558
Definition: pix.h:532
l_ok dewarpaModelStatus(L_DEWARPA *dewa, l_int32 pageno, l_int32 *pvsuccess, l_int32 *phsuccess)
dewarpaModelStatus()
Definition: dewarp2.c:1790
l_ok gplotSimpleXY1(NUMA *nax, NUMA *nay, l_int32 plotstyle, l_int32 outformat, const char *outroot, const char *title)
gplotSimpleXY1()
Definition: gplot.c:668
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:751
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:133
void ptaaDestroy(PTAA **pptaa)
ptaaDestroy()
Definition: ptabasic.c:967
FPIX * fpixRotateOrth(FPIX *fpixs, l_int32 quads)
fpixRotateOrth()
Definition: fpix2.c:1762
PIXA * pixaSelectBySize(PIXA *pixas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
pixaSelectBySize()
Definition: pixafunc1.c:299
l_ok ptaGetPt(PTA *pta, l_int32 index, l_float32 *px, l_float32 *py)
ptaGetPt()
Definition: ptabasic.c:525
PTA * ptaCreateFromNuma(NUMA *nax, NUMA *nay)
ptaCreateFromNuma()
Definition: ptabasic.c:149
l_int32 mincurv
Definition: dewarp.h:170
static l_int32 dewarpGetLineEndPoints(l_int32 h, PTAA *ptaa, PTA **pptal, PTA **pptar)
dewarpGetLineEndPoints()
Definition: dewarp2.c:999
l_ok dewarpPopulateFullRes(L_DEWARP *dew, PIX *pix, l_int32 x, l_int32 y)
dewarpPopulateFullRes()
Definition: dewarp3.c:785
static l_int32 dewarpFilterLineEndPoints(L_DEWARP *dew, PTA *ptal1, PTA *ptar1, PTA **pptal2, PTA **pptar2)
dewarpFilterLineEndPoints()
Definition: dewarp2.c:1078
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:966
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:835
l_int32 rightcurv
Definition: dewarp.h:175
PTAA * dewarpGetTextlineCenters(PIX *pixs, l_int32 debugflag)
dewarpGetTextlineCenters()
Definition: dewarp2.c:762
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
struct FPix * sampydispar
Definition: dewarp.h:157
PIX * pixRotateOrth(PIX *pixs, l_int32 quads)
pixRotateOrth()
Definition: rotateorth.c:72
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:483
Definition: pix.h:454
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_int32 vsuccess
Definition: dewarp.h:180
PIX * pixSeedfillBinary(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillBinary()
Definition: seedfill.c:243
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
l_ok numaGetMin(NUMA *na, l_float32 *pminval, l_int32 *piminloc)
numaGetMin()
Definition: numafunc1.c:444
l_ok applyLinearFit(l_float32 a, l_float32 b, l_float32 x, l_float32 *py)
applyLinearFit()
Definition: ptafunc1.c:1691
l_int32 hsuccess
Definition: dewarp.h:181
l_int32 ptaaGetCount(PTAA *ptaa)
ptaaGetCount()
Definition: ptabasic.c:1074
static l_int32 dewarpQuadraticLSF(PTA *ptad, l_float32 *pa, l_float32 *pb, l_float32 *pc, l_float32 *pmederr)
dewarpQuadraticLSF()
Definition: dewarp2.c:1296
l_int32 hvalid
Definition: dewarp.h:184
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:153
l_int32 leftslope
Definition: dewarp.h:172
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:865
l_ok ptaWriteDebug(const char *filename, PTA *pta, l_int32 type)
ptaWriteDebug()
Definition: ptabasic.c:782
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:672
PIX * pixDisplayPta(PIX *pixd, PIX *pixs, PTA *pta)
pixDisplayPta()
Definition: ptafunc1.c:2364
struct FPix * fullvdispar
Definition: dewarp.h:158
Definition: pix.h:134
Definition: pix.h:719
l_ok numaGetMedianDevFromMedian(NUMA *na, l_float32 *pmed, l_float32 *pdev)
numaGetMedianDevFromMedian()
Definition: numafunc1.c:3241
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:192
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
PTA * ptaSelectRange(PTA *ptas, l_int32 first, l_int32 last)
ptaSelectRange()
Definition: ptafunc1.c:384
l_ok numaReplaceNumber(NUMA *na, l_int32 index, l_float32 val)
numaReplaceNumber()
Definition: numabasic.c:602
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:278
l_int32 rightslope
Definition: dewarp.h:173
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:718
l_ok ptaGetMinMax(PTA *pta, l_float32 *pxmin, l_float32 *pymin, l_float32 *pxmax, l_float32 *pymax)
ptaGetMinMax()
Definition: ptafunc1.c:856
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:2497
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:555
l_ok ptaaAddPta(PTAA *ptaa, PTA *pta, l_int32 copyflag)
ptaaAddPta()
Definition: ptabasic.c:1004
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:486
static l_int32 pixRenderHorizEndPoints(PIX *pixs, PTA *ptal, PTA *ptar, l_uint32 color)
pixRenderHorizEndPoints()
Definition: dewarp2.c:1861
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2021
l_int32 w
Definition: dewarp.h:163
static l_int32 pixRenderMidYs(PIX *pixs, NUMA *namidys, l_int32 linew)
pixRenderMidYs()
Definition: dewarp2.c:1824
PTA * generatePtaFilledCircle(l_int32 radius)
generatePtaFilledCircle()
Definition: graphics.c:823
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:373
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:408
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:165
struct Numa * nacurves
Definition: dewarp.h:162
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:631
l_ok numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:599
PTAA * ptaaCreate(l_int32 n)
ptaaCreate()
Definition: ptabasic.c:939
L_DEWARP * dewarpaGetDewarp(L_DEWARPA *dewa, l_int32 index)
dewarpaGetDewarp()
Definition: dewarp1.c:902
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:575
PTA * ptaTranspose(PTA *ptas)
ptaTranspose()
Definition: ptafunc1.c:288
Definition: pix.h:517
Definition: pix.h:582
l_ok ptaaWriteDebug(const char *filename, PTAA *ptaa, l_int32 type)
ptaaWriteDebug()
Definition: ptabasic.c:1415
PIXCMAP * pixcmapCreateRandom(l_int32 depth, l_int32 hasblack, l_int32 haswhite)
pixcmapCreateRandom()
Definition: colormap.c:158
PIX * fpixRenderContours(FPIX *fpixs, l_float32 incr, l_float32 proxim)
fpixRenderContours()
Definition: graphics.c:2783