comparison mupdf-source/thirdparty/leptonica/src/dewarp3.c @ 2:b50eed0cc0ef upstream

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