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