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 
27 /*!
28  * \file dewarp3.c
29  * <pre>
30  *
31  *    Applying and stripping the page disparity model
32  *
33  *      Apply disparity array to pix
34  *          l_int32            dewarpaApplyDisparity()
35  *          static l_int32     dewarpaApplyInit()
36  *          static PIX        *pixApplyVertDisparity()
37  *          static PIX        *pixApplyHorizDisparity()
38  *
39  *      Apply disparity array to boxa
40  *          l_int32            dewarpaApplyDisparityBoxa()
41  *          static BOXA       *boxaApplyDisparity()
42  *
43  *      Stripping out data and populating full res disparity
44  *          l_int32            dewarpMinimize()
45  *          l_int32            dewarpPopulateFullRes()
46  *
47  *      Static functions not presently in use
48  *          static FPIX       *fpixSampledDisparity()
49  *          static FPIX       *fpixExtraHorizDisparity()
50  *
51  * </pre>
52  */
53 
54 #include <math.h>
55 #include "allheaders.h"
56 
57 static l_int32 dewarpaApplyInit(L_DEWARPA *dewa, l_int32 pageno, PIX *pixs,
58                                 l_int32 x, l_int32 y, L_DEWARP **pdew,
59                                 const char *debugfile);
60 static PIX *pixApplyVertDisparity(L_DEWARP *dew, PIX *pixs, l_int32 grayin);
61 static PIX * pixApplyHorizDisparity(L_DEWARP *dew, PIX *pixs, l_int32 grayin);
62 static BOXA *boxaApplyDisparity(L_DEWARP *dew, BOXA *boxa, l_int32 direction,
63                                 l_int32 mapdir);
64 
65 
66 
67 /*----------------------------------------------------------------------*
68  *                 Apply warping disparity array to pixa                *
69  *----------------------------------------------------------------------*/
70 /*!
71  * \brief   dewarpaApplyDisparity()
72  *
73  * \param[in]    dewa
74  * \param[in]    pageno of page model to be used; may be a ref model
75  * \param[in]    pixs image to be modified; can be 1, 8 or 32 bpp
76  * \param[in]    grayin gray value, from 0 to 255, for pixels brought in;
77  *                      use -1 to use pixels on the boundary of pixs
78  * \param[in]    x, y origin for generation of disparity arrays
79  * \param[out]   ppixd disparity corrected image
80  * \param[in]    debugfile use NULL to skip writing this
81  * \return  0 if OK, 1 on error no models or ref models available
82  *
83  * <pre>
84  * Notes:
85  *      (1) This applies the disparity arrays to the specified image.
86  *      (2) Specify gray color for pixels brought in from the outside:
87  *          0 is black, 255 is white.  Use -1 to select pixels from the
88  *          boundary of the source image.
89  *      (3) If the models and ref models have not been validated, this
90  *          will do so by calling dewarpaInsertRefModels().
91  *      (4) This works with both stripped and full resolution page models.
92  *          If the full res disparity array(s) are missing, they are remade.
93  *      (5) The caller must handle errors that are returned because there
94  *          are no valid models or ref models for the page -- typically
95  *          by using the input pixs.
96  *      (6) If there is no model for %pageno, this will use the model for
97  *          'refpage' and put the result in the dew for %pageno.
98  *      (7) This populates the full resolution disparity arrays if
99  *          necessary.  If x and/or y are positive, they are used,
100  *          in conjunction with pixs, to determine the required
101  *          slope-based extension of the full resolution disparity
102  *          arrays in each direction.  When (x,y) == (0,0), all
103  *          extension is to the right and down.  Nonzero values of (x,y)
104  *          are useful for dewarping when pixs is deliberately undercropped.
105  *      (8) Important: when applying disparity to a number of images,
106  *          after calling this function and saving the resulting pixd,
107  *          you should call dewarpMinimize(dew) on the dew for %pageno.
108  *          This will remove pixs and pixd (or their clones) stored in dew,
109  *          as well as the full resolution disparity arrays.  Together,
110  *          these hold approximately 16 bytes for each pixel in pixs.
111  * </pre>
112  */
113 l_int32
dewarpaApplyDisparity(L_DEWARPA * dewa,l_int32 pageno,PIX * pixs,l_int32 grayin,l_int32 x,l_int32 y,PIX ** ppixd,const char * debugfile)114 dewarpaApplyDisparity(L_DEWARPA   *dewa,
115                       l_int32      pageno,
116                       PIX         *pixs,
117                       l_int32      grayin,
118                       l_int32      x,
119                       l_int32      y,
120                       PIX        **ppixd,
121                       const char  *debugfile)
122 {
123 L_DEWARP  *dew1, *dew;
124 PIX       *pixv, *pixh;
125 
126     PROCNAME("dewarpaApplyDisparity");
127 
128         /* Initialize the output with the input, so we'll have that
129          * in case we can't apply the page model. */
130     if (!ppixd)
131         return ERROR_INT("&pixd not defined", procName, 1);
132     *ppixd = pixClone(pixs);
133     if (grayin > 255) {
134         L_WARNING("invalid grayin = %d; clipping at 255\n", procName, grayin);
135         grayin = 255;
136     }
137 
138         /* Find the appropriate dew to use and fully populate its array(s) */
139     if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile))
140         return ERROR_INT("no model available", procName, 1);
141 
142         /* Correct for vertical disparity and save the result */
143     if ((pixv = pixApplyVertDisparity(dew, pixs, grayin)) == NULL) {
144         dewarpMinimize(dew);
145         return ERROR_INT("pixv not made", procName, 1);
146     }
147     pixDestroy(ppixd);
148     *ppixd = pixv;
149     if (debugfile) {
150         pixDisplayWithTitle(pixv, 300, 0, "pixv", 1);
151         lept_rmdir("lept/dewapply");  /* remove previous images */
152         lept_mkdir("lept/dewapply");
153         pixWriteDebug("/tmp/lept/dewapply/001.png", pixs, IFF_PNG);
154         pixWriteDebug("/tmp/lept/dewapply/002.png", pixv, IFF_PNG);
155     }
156 
157         /* Optionally, correct for horizontal disparity */
158     if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) {
159         if (dew->hvalid == FALSE) {
160             L_INFO("invalid horiz model for page %d\n", procName, pageno);
161         } else {
162             if ((pixh = pixApplyHorizDisparity(dew, pixv, grayin)) != NULL) {
163                 pixDestroy(ppixd);
164                 *ppixd = pixh;
165                 if (debugfile) {
166                     pixDisplayWithTitle(pixh, 600, 0, "pixh", 1);
167                     pixWriteDebug("/tmp/lept/dewapply/003.png", pixh, IFF_PNG);
168                 }
169             } else {
170                 L_ERROR("horiz disparity failed on page %d\n",
171                         procName, pageno);
172             }
173         }
174     }
175 
176     if (debugfile) {
177         dew1 = dewarpaGetDewarp(dewa, pageno);
178         dewarpDebug(dew1, "lept/dewapply", 0);
179         convertFilesToPdf("/tmp/lept/dewapply", NULL, 250, 1.0, 0, 0,
180                          "Dewarp Apply Disparity", debugfile);
181         fprintf(stderr, "pdf file: %s\n", debugfile);
182     }
183 
184         /* Get rid of the large full res disparity arrays */
185     dewarpMinimize(dew);
186 
187     return 0;
188 }
189 
190 
191 /*!
192  * \brief   dewarpaApplyInit()
193  *
194  * \param[in]    dewa
195  * \param[in]    pageno of page model to be used; may be a ref model
196  * \param[in]    pixs image to be modified; can be 1, 8 or 32 bpp
197  * \param[in]    x, y origin for generation of disparity arrays
198  * \param[out]   pdew dewarp to be used for this page
199  * \param[in]    debugfile use NULL to skip writing this
200  * \return  0 if OK, 1 on error no models or ref models available
201  *
202  * <pre>
203  * Notes:
204  *      (1) This prepares pixs for being dewarped.  It returns 1 if
205  *          no dewarping model exists.
206  *      (2) The returned %dew contains the model to be used for this page
207  *          image.  The %dew is owned by dewa; do not destroy.
208  *      (3) If both the 'useboth' and 'check_columns' fields are true,
209  *          this checks for multiple text columns and if found, sets
210  *          the 'skip_horiz' field in the %dew for this page.
211  * </pre>
212  */
213 static l_int32
dewarpaApplyInit(L_DEWARPA * dewa,l_int32 pageno,PIX * pixs,l_int32 x,l_int32 y,L_DEWARP ** pdew,const char * debugfile)214 dewarpaApplyInit(L_DEWARPA   *dewa,
215                  l_int32      pageno,
216                  PIX         *pixs,
217                  l_int32      x,
218                  l_int32      y,
219                  L_DEWARP   **pdew,
220                  const char  *debugfile)
221 {
222 l_int32    ncols, debug;
223 L_DEWARP  *dew1, *dew2;
224 PIX       *pix1;
225 
226     PROCNAME("dewarpaApplyInit");
227 
228     if (!pdew)
229         return ERROR_INT("&dew not defined", procName, 1);
230     *pdew = NULL;
231 
232     if (!dewa)
233         return ERROR_INT("dewa not defined", procName, 1);
234     if (pageno < 0 || pageno > dewa->maxpage)
235         return ERROR_INT("invalid pageno", procName, 1);
236     if (!pixs)
237         return ERROR_INT("pixs not defined", procName, 1);
238     if (x < 0) x = 0;
239     if (y < 0) y = 0;
240     debug = (debugfile) ? 1 : 0;
241 
242         /* Make sure all models are valid and all refmodels have
243          * been added to dewa */
244     if (dewa->modelsready == FALSE)
245         dewarpaInsertRefModels(dewa, 0, debug);
246 
247         /* Check for the existence of a valid model; we don't expect
248          * all pages to have them. */
249     if ((dew1 = dewarpaGetDewarp(dewa, pageno)) == NULL) {
250         L_INFO("no valid dew model for page %d\n", procName, pageno);
251         return 1;
252     }
253 
254         /* Get the page model that we will use and sanity-check that
255          * it is valid.  The ultimate result will be put in dew1->pixd. */
256     if (dew1->hasref)  /* point to another page with a model */
257         dew2 = dewarpaGetDewarp(dewa, dew1->refpage);
258     else
259         dew2 = dew1;
260     if (dew2->vvalid == FALSE)
261         return ERROR_INT("no model; shouldn't happen", procName, 1);
262     *pdew = dew2;
263 
264         /* If check_columns is TRUE and useboth is TRUE, check for
265          * multiple columns.  If there is more than one column, we
266          * only apply vertical disparity. */
267     if (dewa->useboth && dewa->check_columns) {
268         pix1 = pixConvertTo1(pixs, 140);
269         pixCountTextColumns(pix1, 0.3, 0.5, 0.1, &ncols, NULL);
270         pixDestroy(&pix1);
271         if (ncols > 1) {
272             L_INFO("found %d columns; not correcting horiz disparity\n",
273                    procName, ncols);
274             dew2->skip_horiz = TRUE;
275         } else {
276             dew2->skip_horiz = FALSE;
277         }
278     }
279 
280         /* Generate the full res disparity arrays if they don't exist
281          * (e.g., if they've been minimized or read from file), or if
282          * they are too small for the current image.  */
283     dewarpPopulateFullRes(dew2, pixs, x, y);
284     return 0;
285 }
286 
287 
288 /*!
289  * \brief   pixApplyVertDisparity()
290  *
291  * \param[in]    dew
292  * \param[in]    pixs 1, 8 or 32 bpp
293  * \param[in]    grayin gray value, from 0 to 255, for pixels brought in;
294  *                      use -1 to use pixels on the boundary of pixs
295  * \return  pixd modified to remove vertical disparity, or NULL on error
296  *
297  * <pre>
298  * Notes:
299  *      (1) This applies the vertical disparity array to the specified
300  *          image.  For src pixels above the image, we use the pixels
301  *          in the first raster line.
302  *      (2) Specify gray color for pixels brought in from the outside:
303  *          0 is black, 255 is white.  Use -1 to select pixels from the
304  *          boundary of the source image.
305  * </pre>
306  */
307 static PIX *
pixApplyVertDisparity(L_DEWARP * dew,PIX * pixs,l_int32 grayin)308 pixApplyVertDisparity(L_DEWARP  *dew,
309                       PIX       *pixs,
310                       l_int32    grayin)
311 {
312 l_int32     i, j, w, h, d, fw, fh, wpld, wplf, isrc, val8;
313 l_uint32   *datad, *lined;
314 l_float32  *dataf, *linef;
315 void      **lineptrs;
316 FPIX       *fpix;
317 PIX        *pixd;
318 
319     PROCNAME("pixApplyVertDisparity");
320 
321     if (!dew)
322         return (PIX *)ERROR_PTR("dew not defined", procName, NULL);
323     if (!pixs)
324         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
325     pixGetDimensions(pixs, &w, &h, &d);
326     if (d != 1 && d != 8 && d != 32)
327         return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL);
328     if ((fpix = dew->fullvdispar) == NULL)
329         return (PIX *)ERROR_PTR("fullvdispar not defined", procName, NULL);
330     fpixGetDimensions(fpix, &fw, &fh);
331     if (fw < w || fh < h) {
332         fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h);
333         return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL);
334     }
335 
336         /* Two choices for requested pixels outside pixs: (1) use pixels'
337          * from the boundary of pixs; use white or light gray pixels. */
338     pixd = pixCreateTemplate(pixs);
339     if (grayin >= 0)
340         pixSetAllGray(pixd, grayin);
341     datad = pixGetData(pixd);
342     dataf = fpixGetData(fpix);
343     wpld = pixGetWpl(pixd);
344     wplf = fpixGetWpl(fpix);
345     if (d == 1) {
346         lineptrs = pixGetLinePtrs(pixs, NULL);
347         for (i = 0; i < h; i++) {
348             lined = datad + i * wpld;
349             linef = dataf + i * wplf;
350             for (j = 0; j < w; j++) {
351                 isrc = (l_int32)(i - linef[j] + 0.5);
352                 if (grayin < 0)  /* use value at boundary if outside */
353                     isrc = L_MIN(L_MAX(isrc, 0), h - 1);
354                 if (isrc >= 0 && isrc < h) {  /* remains gray if outside */
355                     if (GET_DATA_BIT(lineptrs[isrc], j))
356                         SET_DATA_BIT(lined, j);
357                 }
358             }
359         }
360     } else if (d == 8) {
361         lineptrs = pixGetLinePtrs(pixs, NULL);
362         for (i = 0; i < h; i++) {
363             lined = datad + i * wpld;
364             linef = dataf + i * wplf;
365             for (j = 0; j < w; j++) {
366                 isrc = (l_int32)(i - linef[j] + 0.5);
367                 if (grayin < 0)
368                     isrc = L_MIN(L_MAX(isrc, 0), h - 1);
369                 if (isrc >= 0 && isrc < h) {
370                     val8 = GET_DATA_BYTE(lineptrs[isrc], j);
371                     SET_DATA_BYTE(lined, j, val8);
372                 }
373             }
374         }
375     } else {  /* d == 32 */
376         lineptrs = pixGetLinePtrs(pixs, NULL);
377         for (i = 0; i < h; i++) {
378             lined = datad + i * wpld;
379             linef = dataf + i * wplf;
380             for (j = 0; j < w; j++) {
381                 isrc = (l_int32)(i - linef[j] + 0.5);
382                 if (grayin < 0)
383                     isrc = L_MIN(L_MAX(isrc, 0), h - 1);
384                 if (isrc >= 0 && isrc < h)
385                     lined[j] = GET_DATA_FOUR_BYTES(lineptrs[isrc], j);
386             }
387         }
388     }
389 
390     LEPT_FREE(lineptrs);
391     return pixd;
392 }
393 
394 
395 /*!
396  * \brief   pixApplyHorizDisparity()
397  *
398  * \param[in]    dew
399  * \param[in]    pixs 1, 8 or 32 bpp
400  * \param[in]    grayin gray value, from 0 to 255, for pixels brought in;
401  *                      use -1 to use pixels on the boundary of pixs
402  * \return  pixd modified to remove horizontal disparity if possible,
403  *              or NULL on error.
404  *
405  * <pre>
406  * Notes:
407  *      (1) This applies the horizontal disparity array to the specified
408  *          image.
409  *      (2) Specify gray color for pixels brought in from the outside:
410  *          0 is black, 255 is white.  Use -1 to select pixels from the
411  *          boundary of the source image.
412  *      (3) The input pixs has already been corrected for vertical disparity.
413  *          If the horizontal disparity array doesn't exist, this returns
414  *          a clone of %pixs.
415  * </pre>
416  */
417 static PIX *
pixApplyHorizDisparity(L_DEWARP * dew,PIX * pixs,l_int32 grayin)418 pixApplyHorizDisparity(L_DEWARP  *dew,
419                        PIX       *pixs,
420                        l_int32    grayin)
421 {
422 l_int32     i, j, w, h, d, fw, fh, wpls, wpld, wplf, jsrc, val8;
423 l_uint32   *datas, *lines, *datad, *lined;
424 l_float32  *dataf, *linef;
425 FPIX       *fpix;
426 PIX        *pixd;
427 
428     PROCNAME("pixApplyHorizDisparity");
429 
430     if (!dew)
431         return (PIX *)ERROR_PTR("dew not defined", procName, pixs);
432     if (!pixs)
433         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
434     pixGetDimensions(pixs, &w, &h, &d);
435     if (d != 1 && d != 8 && d != 32)
436         return (PIX *)ERROR_PTR("pix not 1, 8 or 32 bpp", procName, NULL);
437     if ((fpix = dew->fullhdispar) == NULL)
438         return (PIX *)ERROR_PTR("fullhdispar not defined", procName, NULL);
439     fpixGetDimensions(fpix, &fw, &fh);
440     if (fw < w || fh < h) {
441         fprintf(stderr, "fw = %d, w = %d, fh = %d, h = %d\n", fw, w, fh, h);
442         return (PIX *)ERROR_PTR("invalid fpix size", procName, NULL);
443     }
444 
445         /* Two choices for requested pixels outside pixs: (1) use pixels'
446          * from the boundary of pixs; use white or light gray pixels. */
447     pixd = pixCreateTemplate(pixs);
448     if (grayin >= 0)
449         pixSetAllGray(pixd, grayin);
450     datas = pixGetData(pixs);
451     datad = pixGetData(pixd);
452     dataf = fpixGetData(fpix);
453     wpls = pixGetWpl(pixs);
454     wpld = pixGetWpl(pixd);
455     wplf = fpixGetWpl(fpix);
456     if (d == 1) {
457         for (i = 0; i < h; i++) {
458             lines = datas + i * wpls;
459             lined = datad + i * wpld;
460             linef = dataf + i * wplf;
461             for (j = 0; j < w; j++) {
462                 jsrc = (l_int32)(j - linef[j] + 0.5);
463                 if (grayin < 0)  /* use value at boundary if outside */
464                     jsrc = L_MIN(L_MAX(jsrc, 0), w - 1);
465                 if (jsrc >= 0 && jsrc < w) {  /* remains gray if outside */
466                     if (GET_DATA_BIT(lines, jsrc))
467                         SET_DATA_BIT(lined, j);
468                 }
469             }
470         }
471     } else if (d == 8) {
472         for (i = 0; i < h; i++) {
473             lines = datas + i * wpls;
474             lined = datad + i * wpld;
475             linef = dataf + i * wplf;
476             for (j = 0; j < w; j++) {
477                 jsrc = (l_int32)(j - linef[j] + 0.5);
478                 if (grayin < 0)
479                     jsrc = L_MIN(L_MAX(jsrc, 0), w - 1);
480                 if (jsrc >= 0 && jsrc < w) {
481                     val8 = GET_DATA_BYTE(lines, jsrc);
482                     SET_DATA_BYTE(lined, j, val8);
483                 }
484             }
485         }
486     } else {  /* d == 32 */
487         for (i = 0; i < h; i++) {
488             lines = datas + i * wpls;
489             lined = datad + i * wpld;
490             linef = dataf + i * wplf;
491             for (j = 0; j < w; j++) {
492                 jsrc = (l_int32)(j - linef[j] + 0.5);
493                 if (grayin < 0)
494                     jsrc = L_MIN(L_MAX(jsrc, 0), w - 1);
495                 if (jsrc >= 0 && jsrc < w)
496                     lined[j] = lines[jsrc];
497             }
498         }
499     }
500 
501     return pixd;
502 }
503 
504 
505 /*----------------------------------------------------------------------*
506  *                 Apply warping disparity array to boxa                *
507  *----------------------------------------------------------------------*/
508 /*!
509  * \brief   dewarpaApplyDisparityBoxa()
510  *
511  * \param[in]    dewa
512  * \param[in]    pageno of page model to be used; may be a ref model
513  * \param[in]    pixs initial pix reference; for alignment and debugging
514  * \param[in]    boxas boxa to be mapped
515  * \param[in]    mapdir 1 if mapping forward from original to dewarped;
516  *                      0 if backward
517  * \param[in]    x, y origin for generation of disparity arrays with
518  *                    respect to the source region
519  * \param[out]   pboxad disparity corrected boxa
520  * \param[in]    debugfile use NULL to skip writing this
521  * \return  0 if OK, 1 on error no models or ref models available
522  *
523  * <pre>
524  * Notes:
525  *      (1) This applies the disparity arrays in one of two mapping directions
526  *          to the specified boxa.  It can be used in the backward direction
527  *          to locate a box in the original coordinates that would have
528  *          been dewarped to to the specified image.
529  *      (2) If there is no model for %pageno, this will use the model for
530  *          'refpage' and put the result in the dew for %pageno.
531  *      (3) This works with both stripped and full resolution page models.
532  *          If the full res disparity array(s) are missing, they are remade.
533  *      (4) If an error occurs, a copy of the input boxa is returned.
534  * </pre>
535  */
536 l_int32
dewarpaApplyDisparityBoxa(L_DEWARPA * dewa,l_int32 pageno,PIX * pixs,BOXA * boxas,l_int32 mapdir,l_int32 x,l_int32 y,BOXA ** pboxad,const char * debugfile)537 dewarpaApplyDisparityBoxa(L_DEWARPA   *dewa,
538                           l_int32      pageno,
539                           PIX         *pixs,
540                           BOXA        *boxas,
541                           l_int32      mapdir,
542                           l_int32      x,
543                           l_int32      y,
544                           BOXA       **pboxad,
545                           const char  *debugfile)
546 {
547 l_int32    debug_out;
548 L_DEWARP  *dew1, *dew;
549 BOXA      *boxav, *boxah;
550 PIX       *pixv, *pixh;
551 
552     PROCNAME("dewarpaApplyDisparityBoxa");
553 
554         /* Initialize the output with the input, so we'll have that
555          * in case we can't apply the page model. */
556     if (!pboxad)
557         return ERROR_INT("&boxad not defined", procName, 1);
558     *pboxad = boxaCopy(boxas, L_CLONE);
559 
560         /* Find the appropriate dew to use and fully populate its array(s) */
561     if (dewarpaApplyInit(dewa, pageno, pixs, x, y, &dew, debugfile))
562         return ERROR_INT("no model available", procName, 1);
563 
564         /* Correct for vertical disparity and save the result */
565     if ((boxav = boxaApplyDisparity(dew, boxas, L_VERT, mapdir)) == NULL) {
566         dewarpMinimize(dew);
567         return ERROR_INT("boxa1 not made", procName, 1);
568     }
569     boxaDestroy(pboxad);
570     *pboxad = boxav;
571     pixv = NULL;
572     pixh = NULL;
573     if (debugfile && mapdir != 1)
574         L_INFO("Reverse map direction; no debug output\n", procName);
575     debug_out = debugfile && (mapdir == 1);
576     if (debug_out) {
577         PIX  *pix1;
578         lept_rmdir("lept/dewboxa");  /* remove previous images */
579         lept_mkdir("lept/dewboxa");
580         pix1 = pixConvertTo32(pixs);
581         pixRenderBoxaArb(pix1, boxas, 2, 255, 0, 0);
582         pixWriteDebug("/tmp/lept/dewboxa/01.png", pix1, IFF_PNG);
583         pixDestroy(&pix1);
584         pixv = pixApplyVertDisparity(dew, pixs, 255);
585         pix1 = pixConvertTo32(pixv);
586         pixRenderBoxaArb(pix1, boxav, 2, 0, 255, 0);
587         pixWriteDebug("/tmp/lept/dewboxa/02.png", pix1, IFF_PNG);
588         pixDestroy(&pix1);
589     }
590 
591         /* Optionally, correct for horizontal disparity */
592     if (dewa->useboth && dew->hsuccess && !dew->skip_horiz) {
593         if (dew->hvalid == FALSE) {
594             L_INFO("invalid horiz model for page %d\n", procName, pageno);
595         } else {
596             boxah = boxaApplyDisparity(dew, boxav, L_HORIZ, mapdir);
597             if (!boxah) {
598                 L_ERROR("horiz disparity fails on page %d\n", procName, pageno);
599             } else {
600                 boxaDestroy(pboxad);
601                 *pboxad = boxah;
602                 if (debug_out) {
603                     PIX  *pix1;
604                     pixh = pixApplyHorizDisparity(dew, pixv, 255);
605                     pix1 = pixConvertTo32(pixh);
606                     pixRenderBoxaArb(pix1, boxah, 2, 0, 0, 255);
607                     pixWriteDebug("/tmp/lept/dewboxa/03.png", pix1, IFF_PNG);
608                     pixDestroy(&pixh);
609                     pixDestroy(&pix1);
610                 }
611             }
612         }
613     }
614 
615     if (debug_out) {
616         pixDestroy(&pixv);
617         dew1 = dewarpaGetDewarp(dewa, pageno);
618         dewarpDebug(dew1, "lept/dewapply", 0);
619         convertFilesToPdf("/tmp/lept/dewboxa", NULL, 135, 1.0, 0, 0,
620                          "Dewarp Apply Disparity Boxa", debugfile);
621         fprintf(stderr, "Dewarp Apply Disparity Boxa pdf file: %s\n",
622                 debugfile);
623     }
624 
625         /* Get rid of the large full res disparity arrays */
626     dewarpMinimize(dew);
627 
628     return 0;
629 }
630 
631 
632 /*!
633  * \brief   boxaApplyDisparity()
634  *
635  * \param[in]    dew
636  * \param[in]    boxa
637  * \param[in]    direction L_HORIZ or L_VERT
638  * \param[in]    mapdir 1 if mapping forward from original to dewarped;
639  *                      0 if backward
640  * \return  boxad modified by the disparity, or NULL on error
641  */
642 static BOXA *
boxaApplyDisparity(L_DEWARP * dew,BOXA * boxa,l_int32 direction,l_int32 mapdir)643 boxaApplyDisparity(L_DEWARP  *dew,
644                    BOXA      *boxa,
645                    l_int32    direction,
646                    l_int32    mapdir)
647 {
648 l_int32     x, y, w, h, ib, ip, nbox, wpl;
649 l_float32   xn, yn;
650 l_float32  *data, *line;
651 BOX        *boxs, *boxd;
652 BOXA       *boxad;
653 FPIX       *fpix;
654 PTA        *ptas, *ptad;
655 
656     PROCNAME("boxaApplyDisparity");
657 
658     if (!dew)
659         return (BOXA *)ERROR_PTR("dew not defined", procName, NULL);
660     if (!boxa)
661         return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
662     if (direction == L_VERT)
663         fpix = dew->fullvdispar;
664     else if (direction == L_HORIZ)
665         fpix = dew->fullhdispar;
666     else
667         return (BOXA *)ERROR_PTR("invalid direction", procName, NULL);
668     if (!fpix)
669         return (BOXA *)ERROR_PTR("full disparity not defined", procName, NULL);
670     fpixGetDimensions(fpix, &w, &h);
671 
672         /* Clip the output to the positive quadrant because all box
673          * coordinates must be non-negative. */
674     data = fpixGetData(fpix);
675     wpl = fpixGetWpl(fpix);
676     nbox = boxaGetCount(boxa);
677     boxad = boxaCreate(nbox);
678     for (ib = 0; ib < nbox; ib++) {
679         boxs = boxaGetBox(boxa, ib, L_COPY);
680         ptas = boxConvertToPta(boxs, 4);
681         ptad = ptaCreate(4);
682         for (ip = 0; ip < 4; ip++) {
683             ptaGetIPt(ptas, ip, &x, &y);
684             line = data + y * wpl;
685             if (direction == L_VERT) {
686                 if (mapdir == 0)
687                     yn = y - line[x];
688                 else
689                     yn = y + line[x];
690                 yn = L_MAX(0, yn);
691                 ptaAddPt(ptad, x, yn);
692             } else {  /* direction == L_HORIZ */
693                 if (mapdir == 0)
694                     xn = x - line[x];
695                 else
696                     xn = x + line[x];
697                 xn = L_MAX(0, xn);
698                 ptaAddPt(ptad, xn, y);
699             }
700         }
701         boxd = ptaConvertToBox(ptad);
702         boxaAddBox(boxad, boxd, L_INSERT);
703         boxDestroy(&boxs);
704         ptaDestroy(&ptas);
705         ptaDestroy(&ptad);
706     }
707 
708     return boxad;
709 }
710 
711 
712 /*----------------------------------------------------------------------*
713  *          Stripping out data and populating full res disparity        *
714  *----------------------------------------------------------------------*/
715 /*!
716  * \brief   dewarpMinimize()
717  *
718  * \param[in]    dew
719  * \return  0 if OK, 1 on error
720  *
721  * <pre>
722  * Notes:
723  *      (1) This removes all data that is not needed for serialization.
724  *          It keeps the subsampled disparity array(s), so the full
725  *          resolution arrays can be reconstructed.
726  * </pre>
727  */
728 l_int32
dewarpMinimize(L_DEWARP * dew)729 dewarpMinimize(L_DEWARP  *dew)
730 {
731 L_DEWARP  *dewt;
732 
733     PROCNAME("dewarpMinimize");
734 
735     if (!dew)
736         return ERROR_INT("dew not defined", procName, 1);
737 
738         /* If dew is a ref, minimize the actual dewarp */
739     if (dew->hasref)
740         dewt = dewarpaGetDewarp(dew->dewa, dew->refpage);
741     else
742         dewt = dew;
743     if (!dewt)
744         return ERROR_INT("dewt not found", procName, 1);
745 
746     pixDestroy(&dewt->pixs);
747     fpixDestroy(&dewt->fullvdispar);
748     fpixDestroy(&dewt->fullhdispar);
749     numaDestroy(&dewt->namidys);
750     numaDestroy(&dewt->nacurves);
751     return 0;
752 }
753 
754 
755 /*!
756  * \brief   dewarpPopulateFullRes()
757  *
758  * \param[in]    dew
759  * \param[in]    pix [optional], to give size of actual image
760  * \param[in]    x, y origin for generation of disparity arrays
761  * \return  0 if OK, 1 on error
762  *
763  * <pre>
764  * Notes:
765  *      (1) If the full resolution vertical and horizontal disparity
766  *          arrays do not exist, they are built from the subsampled ones.
767  *      (2) If pixs is not given, the size of the arrays is determined
768  *          by the original image from which the sampled version was
769  *          generated.  Any values of (x,y) are ignored.
770  *      (3) If pixs is given, the full resolution disparity arrays must
771  *          be large enough to accommodate it.
772  *          (a) If the arrays do not exist, the value of (x,y) determines
773  *              the origin of the full resolution arrays without extension,
774  *              relative to pixs.  Thus, (x,y) gives the amount of
775  *              slope extension in (left, top).  The (right, bottom)
776  *              extension is then determined by the size of pixs and
777  *              (x,y); the values should never be < 0.
778  *          (b) If the arrays exist and pixs is too large, the existing
779  *              full res arrays are destroyed and new ones are made,
780  *              again using (x,y) to determine the extension in the
781  *              four directions.
782  * </pre>
783  */
784 l_int32
dewarpPopulateFullRes(L_DEWARP * dew,PIX * pix,l_int32 x,l_int32 y)785 dewarpPopulateFullRes(L_DEWARP  *dew,
786                       PIX       *pix,
787                       l_int32    x,
788                       l_int32    y)
789 {
790 l_int32     width, height, fw, fh, deltaw, deltah, redfactor;
791 FPIX       *fpixt1, *fpixt2;
792 
793     PROCNAME("dewarpPopulateFullRes");
794 
795     if (!dew)
796         return ERROR_INT("dew not defined", procName, 1);
797     if (!dew->sampvdispar)
798         return ERROR_INT("no sampled vert disparity", procName, 1);
799     if (x < 0) x = 0;
800     if (y < 0) y = 0;
801 
802         /* Establish the target size for the full res arrays */
803     if (pix)
804         pixGetDimensions(pix, &width, &height, NULL);
805     else {
806         width = dew->w;
807         height = dew->h;
808     }
809 
810         /* Destroy the existing arrays if they are too small */
811     if (dew->fullvdispar) {
812         fpixGetDimensions(dew->fullvdispar, &fw, &fh);
813         if (width > fw || height > fw)
814             fpixDestroy(&dew->fullvdispar);
815     }
816     if (dew->fullhdispar) {
817         fpixGetDimensions(dew->fullhdispar, &fw, &fh);
818         if (width > fw || height > fw)
819             fpixDestroy(&dew->fullhdispar);
820     }
821 
822         /* Find the required width and height expansion deltas */
823     deltaw = width - dew->sampling * (dew->nx - 1) + 2;
824     deltah = height - dew->sampling * (dew->ny - 1) + 2;
825     redfactor = dew->redfactor;
826     deltaw = redfactor * L_MAX(0, deltaw);
827     deltah = redfactor * L_MAX(0, deltah);
828 
829         /* Generate the full res vertical array if it doesn't exist,
830          * extending it as required to make it big enough.  Use x,y
831          * to determine the amounts on each side. */
832     if (!dew->fullvdispar) {
833         fpixt1 = fpixCopy(NULL, dew->sampvdispar);
834         if (redfactor == 2)
835             fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor);
836         fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor);
837         fpixDestroy(&fpixt1);
838         if (deltah == 0 && deltaw == 0) {
839             dew->fullvdispar = fpixt2;
840         }
841         else {
842             dew->fullvdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x,
843                                                   y, deltah - y);
844             fpixDestroy(&fpixt2);
845         }
846     }
847 
848         /* Similarly, generate the full res horizontal array if it
849          * doesn't exist.  Do this even if useboth == 1, but
850          * not if required to skip running horizontal disparity. */
851     if (!dew->fullhdispar && dew->samphdispar && !dew->skip_horiz) {
852         fpixt1 = fpixCopy(NULL, dew->samphdispar);
853         if (redfactor == 2)
854             fpixAddMultConstant(fpixt1, 0.0, (l_float32)redfactor);
855         fpixt2 = fpixScaleByInteger(fpixt1, dew->sampling * redfactor);
856         fpixDestroy(&fpixt1);
857         if (deltah == 0 && deltaw == 0) {
858             dew->fullhdispar = fpixt2;
859         }
860         else {
861             dew->fullhdispar = fpixAddSlopeBorder(fpixt2, x, deltaw - x,
862                                                   y, deltah - y);
863             fpixDestroy(&fpixt2);
864         }
865     }
866 
867     return 0;
868 }
869 
870 
871 #if 0
872 /*----------------------------------------------------------------------*
873  *                Static functions not presently in use                 *
874  *----------------------------------------------------------------------*/
875 /*!
876  * \brief   fpixSampledDisparity()
877  *
878  * \param[in]    fpixs full resolution disparity model
879  * \param[in]    sampling sampling factor
880  * \return  fpixd sampled disparity model, or NULL on error
881  *
882  * <pre>
883  * Notes:
884  *      (1) This converts full to sampled disparity.
885  *      (2) The input array is sampled at the right and top edges, and
886  *          at every %sampling pixels horizontally and vertically.
887  *      (3) The sampled array may not extend to the right and bottom
888  *          pixels in fpixs.  This will occur if fpixs was generated
889  *          with slope extension because the image on that page was
890  *          larger than normal.  This is fine, because in use the
891  *          sampled array will be interpolated back to full resolution
892  *          and then extended as required.  So the operations of
893  *          sampling and interpolation will be idempotent.
894  *      (4) There must be at least 3 sampled points horizontally and
895  *          vertically.
896  * </pre>
897  */
898 static FPIX *
899 fpixSampledDisparity(FPIX    *fpixs,
900                      l_int32  sampling)
901 {
902 l_int32    w, h, wd, hd, i, j, is, js;
903 l_float32  val;
904 FPIX      *fpixd;
905 
906     PROCNAME("fpixSampledDisparity");
907 
908     if (!fpixs)
909         return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
910     if (sampling < 1)
911         return (FPIX *)ERROR_PTR("sampling < 1", procName, NULL);
912 
913     fpixGetDimensions(fpixs, &w, &h);
914     wd = 1 + (w + sampling - 2) / sampling;
915     hd = 1 + (h + sampling - 2) / sampling;
916     if (wd < 3 || hd < 3)
917         return (FPIX *)ERROR_PTR("wd < 3 or hd < 3", procName, NULL);
918     fpixd = fpixCreate(wd, hd);
919     for (i = 0; i < hd; i++) {
920         is = sampling * i;
921         if (is >= h) continue;
922         for (j = 0; j < wd; j++) {
923             js = sampling * j;
924             if (js >= w) continue;
925             fpixGetPixel(fpixs, js, is, &val);
926             fpixSetPixel(fpixd, j, i, val);
927         }
928     }
929 
930     return fpixd;
931 }
932 
933 
934 /*!
935  * \brief   fpixExtraHorizDisparity()
936  *
937  * \param[in]    fpixv vertical disparity model
938  * \param[in]    factor conversion factor for vertical disparity slope;
939  *                      use 0 for default
940  * \param[out]   pxwid extra width to be added to dewarped pix
941  * \return  fpixh, or NULL on error
942  *
943  * <pre>
944  * Notes:
945  *      (1) This takes the difference in vertical disparity at top
946  *          and bottom of the image, and converts it to an assumed
947  *          horizontal disparity.  In use, we add this to the
948  *          horizontal disparity determined by the left and right
949  *          ends of textlines.
950  *      (2) Usage:
951  *            l_int32 xwid = [extra width to be added to fpix and image]
952  *            FPix *fpix = fpixExtraHorizDisparity(dew->fullvdispar, 0, &xwid);
953  *            fpixLinearCombination(dew->fullhdispar, dew->fullhdispar,
954  *                                  fpix, 1.0, 1.0);
955  * </pre>
956  */
957 static FPIX *
958 fpixExtraHorizDisparity(FPIX      *fpixv,
959                         l_float32  factor,
960                         l_int32   *pxwid)
961 {
962 l_int32     w, h, i, j, fw, wpl, maxloc;
963 l_float32   val1, val2, vdisp, vdisp0, maxval;
964 l_float32  *data, *line, *fadiff;
965 NUMA       *nadiff;
966 FPIX       *fpixh;
967 
968     PROCNAME("fpixExtraHorizDisparity");
969 
970     if (!fpixv)
971         return (FPIX *)ERROR_PTR("fpixv not defined", procName, NULL);
972     if (!pxwid)
973         return (FPIX *)ERROR_PTR("&xwid not defined", procName, NULL);
974     if (factor == 0.0)
975         factor = DEFAULT_SLOPE_FACTOR;
976 
977         /* Estimate horizontal disparity from the vertical disparity
978          * difference between the top and bottom, normalized to the
979          * image height.  Add the maximum value to the width of the
980          * output image, so that all src pixels can be mapped
981          * into the dest. */
982     fpixGetDimensions(fpixv, &w, &h);
983     nadiff = numaCreate(w);
984     for (j = 0; j < w; j++) {
985         fpixGetPixel(fpixv, j, 0, &val1);
986         fpixGetPixel(fpixv, j, h - 1, &val2);
987         vdisp = factor * (val2 - val1) / (l_float32)h;
988         if (j == 0) vdisp0 = vdisp;
989         vdisp = vdisp0 - vdisp;
990         numaAddNumber(nadiff, vdisp);
991     }
992     numaGetMax(nadiff, &maxval, &maxloc);
993     *pxwid = (l_int32)(maxval + 0.5);
994 
995     fw = w + *pxwid;
996     fpixh = fpixCreate(fw, h);
997     data = fpixGetData(fpixh);
998     wpl = fpixGetWpl(fpixh);
999     fadiff = numaGetFArray(nadiff, L_NOCOPY);
1000     for (i = 0; i < h; i++) {
1001         line = data + i * wpl;
1002         for (j = 0; j < fw; j++) {
1003             if (j < maxloc)   /* this may not work for even pages */
1004                 line[j] = fadiff[j];
1005             else  /* keep it at the max value the rest of the way across */
1006                 line[j] = maxval;
1007         }
1008     }
1009 
1010     numaDestroy(&nadiff);
1011     return fpixh;
1012 }
1013 #endif
1014