comparison mupdf-source/thirdparty/leptonica/src/dewarp4.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 dewarp4.c
29 * <pre>
30 *
31 * Single page dewarper
32 *
33 * Reference model (book-level, dewarpa) operations and debugging output
34 *
35 * Top-level single page dewarper
36 * l_int32 dewarpSinglePage()
37 * l_int32 dewarpSinglePageInit()
38 * l_int32 dewarpSinglePageRun()
39 *
40 * Operations on dewarpa
41 * l_int32 dewarpaListPages()
42 * l_int32 dewarpaSetValidModels()
43 * l_int32 dewarpaInsertRefModels()
44 * l_int32 dewarpaStripRefModels()
45 * l_int32 dewarpaRestoreModels()
46 *
47 * Dewarp debugging output
48 * l_int32 dewarpaInfo()
49 * l_int32 dewarpaModelStats()
50 * static l_int32 dewarpaTestForValidModel()
51 * l_int32 dewarpaShowArrays()
52 * l_int32 dewarpDebug()
53 * l_int32 dewarpShowResults()
54 * </pre>
55 */
56
57 #ifdef HAVE_CONFIG_H
58 #include <config_auto.h>
59 #endif /* HAVE_CONFIG_H */
60
61 #include <math.h>
62 #include "allheaders.h"
63
64 static l_int32 dewarpaTestForValidModel(L_DEWARPA *dewa, L_DEWARP *dew,
65 l_int32 notests);
66
67 #ifndef NO_CONSOLE_IO
68 #define DEBUG_INVALID_MODELS 0 /* set this to 1 for debugging */
69 #endif /* !NO_CONSOLE_IO */
70
71 /* Special parameter value */
72 static const l_int32 GrayInValue = 200;
73
74 /*----------------------------------------------------------------------*
75 * Top-level single page dewarper *
76 *----------------------------------------------------------------------*/
77 /*!
78 * \brief dewarpSinglePage()
79 *
80 * \param[in] pixs with text, any depth
81 * \param[in] thresh for global thresh to 1 bpp; ignore otherwise
82 * \param[in] adaptive 1 for adaptive thresh; 0 for global threshold
83 * \param[in] useboth 1 for both horiz and vert; 0 for vertical only
84 * \param[in] check_columns 1 to skip horizontal if multiple columns;
85 * 0 otherwise; default is to skip
86 * \param[out] ppixd dewarped result
87 * \param[out] pdewa [optional] dewa with single page; NULL to skip
88 * \param[in] debug 1 for debugging output, 0 otherwise
89 * \return 0 if OK, 1 on error list of page numbers, or NULL on error
90 *
91 * <pre>
92 * Notes:
93 * (1) Dewarps pixs and returns the result in &pixd.
94 * (2) This uses default values for all model parameters.
95 * (3) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
96 * (4) If it can't build a model, returns a copy of pixs in &pixd.
97 * </pre>
98 */
99 l_ok
100 dewarpSinglePage(PIX *pixs,
101 l_int32 thresh,
102 l_int32 adaptive,
103 l_int32 useboth,
104 l_int32 check_columns,
105 PIX **ppixd,
106 L_DEWARPA **pdewa,
107 l_int32 debug)
108 {
109 L_DEWARPA *dewa;
110 PIX *pixb;
111
112 if (!ppixd)
113 return ERROR_INT("&pixd not defined", __func__, 1);
114 *ppixd = NULL;
115 if (pdewa) *pdewa = NULL;
116 if (!pixs)
117 return ERROR_INT("pixs not defined", __func__, 1);
118
119 dewarpSinglePageInit(pixs, thresh, adaptive, useboth,
120 check_columns, &pixb, &dewa);
121 if (!pixb) {
122 dewarpaDestroy(&dewa);
123 return ERROR_INT("pixb not made", __func__, 1);
124 }
125
126 dewarpSinglePageRun(pixs, pixb, dewa, ppixd, debug);
127
128 if (pdewa)
129 *pdewa = dewa;
130 else
131 dewarpaDestroy(&dewa);
132 pixDestroy(&pixb);
133 return 0;
134 }
135
136
137 /*!
138 * \brief dewarpSinglePageInit()
139 *
140 * \param[in] pixs with text, any depth
141 * \param[in] thresh for global thresh to 1 bpp; ignore otherwise
142 * \param[in] adaptive 1 for adaptive thresh; 0 for global threshold
143 * \param[in] useboth 1 for both horiz and vert; 0 for vertical only
144 * \param[in] check_columns 1 to skip horizontal if multiple columns;
145 * 0 otherwise; default is to skip
146 * \param[out] ppixb 1 bpp debug image
147 * \param[out] pdewa initialized dewa
148 * \return 0 if OK, 1 on error list of page numbers, or NULL on error
149 *
150 * <pre>
151 * Notes:
152 * (1) This binarizes the input pixs if necessary, returning the
153 * binarized image. It also initializes the dewa to default values
154 * for the model parameters.
155 * (2) If pixs is 1 bpp, the parameters %adaptive and %thresh are ignored.
156 * (3) To change the model parameters, call dewarpaSetCurvatures()
157 * before running dewarpSinglePageRun(). For example:
158 * dewarpSinglePageInit(pixs, 0, 1, 1, 1, &pixb, &dewa);
159 * dewarpaSetCurvatures(dewa, 250, -1, -1, 80, 70, 150);
160 * dewarpSinglePageRun(pixs, pixb, dewa, &pixd, 0);
161 * dewarpaDestroy(&dewa);
162 * pixDestroy(&pixb);
163 * </pre>
164 */
165 l_ok
166 dewarpSinglePageInit(PIX *pixs,
167 l_int32 thresh,
168 l_int32 adaptive,
169 l_int32 useboth,
170 l_int32 check_columns,
171 PIX **ppixb,
172 L_DEWARPA **pdewa)
173 {
174 PIX *pix1, *pix2;
175
176 if (ppixb) *ppixb = NULL;
177 if (pdewa) *pdewa = NULL;
178 if (!ppixb || !pdewa)
179 return ERROR_INT("&pixb and &dewa not both defined", __func__, 1);
180 if (!pixs)
181 return ERROR_INT("pixs not defined", __func__, 1);
182
183 /* Generate a binary image, if necessary */
184 if (pixGetDepth(pixs) > 1) {
185 if ((pix1 = pixConvertTo8(pixs, 0)) == NULL)
186 return ERROR_INT("pix1 not made", __func__, 1);
187 if (adaptive)
188 pix2 = pixAdaptThresholdToBinary(pix1, NULL, 1.0);
189 else
190 pix2 = pixThresholdToBinary(pix1, thresh);
191 pixDestroy(&pix1);
192 if (!pix2)
193 return ERROR_INT("pix2 not made", __func__, 1);
194 *ppixb = pix2;
195 } else {
196 *ppixb = pixClone(pixs);
197 }
198
199 *pdewa = dewarpaCreate(1, 0, 1, 0, -1);
200 dewarpaUseBothArrays(*pdewa, useboth);
201 dewarpaSetCheckColumns(*pdewa, check_columns);
202 return 0;
203 }
204
205
206 /*!
207 * \brief dewarpSinglePageRun()
208 *
209 * \param[in] pixs any depth
210 * \param[in] pixb 1 bpp
211 * \param[in] dewa initialized
212 * \param[out] ppixd dewarped result
213 * \param[in] debug 1 for debugging output, 0 otherwise
214 * \return 0 if OK, 1 on error list of page numbers, or NULL on error
215 *
216 * <pre>
217 * Notes:
218 * (1) Dewarps pixs and returns the result in &pixd.
219 * (2) The 1 bpp version %pixb and %dewa are conveniently generated by
220 * dewarpSinglePageInit().
221 * (3) Non-default model parameters must be set before calling this.
222 * (4) If a model cannot be built, this returns a copy of pixs in &pixd.
223 * </pre>
224 */
225 l_ok
226 dewarpSinglePageRun(PIX *pixs,
227 PIX *pixb,
228 L_DEWARPA *dewa,
229 PIX **ppixd,
230 l_int32 debug)
231 {
232 const char *debugfile;
233 l_int32 vsuccess, ret;
234 L_DEWARP *dew;
235
236 if (!ppixd)
237 return ERROR_INT("&pixd not defined", __func__, 1);
238 *ppixd = NULL;
239 if (!pixs)
240 return ERROR_INT("pixs not defined", __func__, 1);
241 if (!pixb)
242 return ERROR_INT("pixb not defined", __func__, 1);
243 if (!dewa)
244 return ERROR_INT("dewa not defined", __func__, 1);
245
246 if (debug)
247 lept_mkdir("lept/dewarp");
248
249 /* Generate the page model */
250 dew = dewarpCreate(pixb, 0);
251 dewarpaInsertDewarp(dewa, dew);
252 debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_model.pdf" : NULL;
253 dewarpBuildPageModel(dew, debugfile);
254 dewarpaModelStatus(dewa, 0, &vsuccess, NULL);
255 if (vsuccess == 0) {
256 L_ERROR("failure to build model for vertical disparity\n", __func__);
257 *ppixd = pixCopy(NULL, pixs);
258 return 0;
259 }
260
261 /* Apply the page model */
262 debugfile = (debug) ? "/tmp/lept/dewarp/singlepage_apply.pdf" : NULL;
263 ret = dewarpaApplyDisparity(dewa, 0, pixs, 255, 0, 0, ppixd, debugfile);
264 if (ret)
265 L_ERROR("invalid model; failure to apply disparity\n", __func__);
266 return 0;
267 }
268
269
270 /*----------------------------------------------------------------------*
271 * Operations on dewarpa *
272 *----------------------------------------------------------------------*/
273 /*!
274 * \brief dewarpaListPages()
275 *
276 * \param[in] dewa populated with dewarp structs for pages
277 * \return 0 if OK, 1 on error list of page numbers, or NULL on error
278 *
279 * <pre>
280 * Notes:
281 * (1) This generates two numas, stored in the dewarpa, that give:
282 * (a) the page number for each dew that has a page model.
283 * (b) the page number for each dew that has either a page
284 * model or a reference model.
285 * It can be called at any time.
286 * (2) It is called by the dewarpa serializer before writing.
287 * </pre>
288 */
289 l_ok
290 dewarpaListPages(L_DEWARPA *dewa)
291 {
292 l_int32 i;
293 L_DEWARP *dew;
294 NUMA *namodels, *napages;
295
296 if (!dewa)
297 return ERROR_INT("dewa not defined", __func__, 1);
298
299 numaDestroy(&dewa->namodels);
300 numaDestroy(&dewa->napages);
301 namodels = numaCreate(dewa->maxpage + 1);
302 napages = numaCreate(dewa->maxpage + 1);
303 dewa->namodels = namodels;
304 dewa->napages = napages;
305 for (i = 0; i <= dewa->maxpage; i++) {
306 if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) {
307 if (dew->hasref == 0)
308 numaAddNumber(namodels, dew->pageno);
309 numaAddNumber(napages, dew->pageno);
310 }
311 }
312 return 0;
313 }
314
315
316 /*!
317 * \brief dewarpaSetValidModels()
318 *
319 * \param[in] dewa
320 * \param[in] notests
321 * \param[in] debug 1 to output information on invalid page models
322 * \return 0 if OK, 1 on error
323 *
324 * <pre>
325 * Notes:
326 * (1) A valid model must meet the rendering requirements, which
327 * include whether or not a vertical disparity model exists
328 * and conditions on curvatures for vertical and horizontal
329 * disparity models.
330 * (2) If %notests == 1, this ignores the curvature constraints
331 * and assumes that all successfully built models are valid.
332 * (3) This function does not need to be called by the application.
333 * It is called by dewarpaInsertRefModels(), which
334 * will destroy all invalid dewarps. Consequently, to inspect
335 * an invalid dewarp model, it must be done before calling
336 * dewarpaInsertRefModels().
337 * </pre>
338 */
339 l_ok
340 dewarpaSetValidModels(L_DEWARPA *dewa,
341 l_int32 notests,
342 l_int32 debug)
343 {
344 l_int32 i, n, maxcurv, diffcurv, diffedge;
345 L_DEWARP *dew;
346
347 if (!dewa)
348 return ERROR_INT("dewa not defined", __func__, 1);
349
350 n = dewa->maxpage + 1;
351 for (i = 0; i < n; i++) {
352 if ((dew = dewarpaGetDewarp(dewa, i)) == NULL)
353 continue;
354
355 if (debug) {
356 if (dew->hasref == 1) {
357 L_INFO("page %d: has only a ref model\n", __func__, i);
358 } else if (dew->vsuccess == 0) {
359 L_INFO("page %d: no model successfully built\n",
360 __func__, i);
361 } else if (!notests) {
362 maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv));
363 diffcurv = dew->maxcurv - dew->mincurv;
364 if (dewa->useboth && !dew->hsuccess)
365 L_INFO("page %d: useboth, but no horiz disparity\n",
366 __func__, i);
367 if (maxcurv > dewa->max_linecurv)
368 L_INFO("page %d: max curvature %d > max_linecurv\n",
369 __func__, i, diffcurv);
370 if (diffcurv < dewa->min_diff_linecurv)
371 L_INFO("page %d: diff curv %d < min_diff_linecurv\n",
372 __func__, i, diffcurv);
373 if (diffcurv > dewa->max_diff_linecurv)
374 L_INFO("page %d: abs diff curv %d > max_diff_linecurv\n",
375 __func__, i, diffcurv);
376 if (dew->hsuccess) {
377 if (L_ABS(dew->leftslope) > dewa->max_edgeslope)
378 L_INFO("page %d: abs left slope %d > max_edgeslope\n",
379 __func__, i, dew->leftslope);
380 if (L_ABS(dew->rightslope) > dewa->max_edgeslope)
381 L_INFO("page %d: abs right slope %d > max_edgeslope\n",
382 __func__, i, dew->rightslope);
383 diffedge = L_ABS(dew->leftcurv - dew->rightcurv);
384 if (L_ABS(dew->leftcurv) > dewa->max_edgecurv)
385 L_INFO("page %d: left curvature %d > max_edgecurv\n",
386 __func__, i, dew->leftcurv);
387 if (L_ABS(dew->rightcurv) > dewa->max_edgecurv)
388 L_INFO("page %d: right curvature %d > max_edgecurv\n",
389 __func__, i, dew->rightcurv);
390 if (diffedge > dewa->max_diff_edgecurv)
391 L_INFO("page %d: abs diff left-right curv %d > "
392 "max_diff_edgecurv\n", __func__, i, diffedge);
393 }
394 }
395 }
396
397 dewarpaTestForValidModel(dewa, dew, notests);
398 }
399
400 return 0;
401 }
402
403
404 /*!
405 * \brief dewarpaInsertRefModels()
406 *
407 * \param[in] dewa
408 * \param[in] notests if 1, ignore curvature constraints on model
409 * \param[in] debug 1 to output information on invalid page models
410 * \return 0 if OK, 1 on error
411 *
412 * <pre>
413 * Notes:
414 * (1) This destroys all dewarp models that are invalid, and then
415 * inserts reference models where possible.
416 * (2) If %notests == 1, this ignores the curvature constraints
417 * and assumes that all successfully built models are valid.
418 * (3) If useboth == 0, it uses the closest valid model within the
419 * distance and parity constraints. If useboth == 1, it tries
420 * to use the closest allowed hvalid model; if it doesn't find
421 * an hvalid model, it uses the closest valid model.
422 * (4) For all pages without a model, this clears out any existing
423 * invalid and reference dewarps, finds the nearest valid model
424 * with the same parity, and inserts an empty dewarp with the
425 * reference page.
426 * (5) Then if it is requested to use both vertical and horizontal
427 * disparity arrays (useboth == 1), it tries to replace any
428 * hvalid == 0 model or reference with an hvalid == 1 reference.
429 * (6) The distance constraint is that any reference model must
430 * be within maxdist. Note that with the parity constraint,
431 * no reference models will be used if maxdist < 2.
432 * (7) This function must be called, even if reference models will
433 * not be used. It should be called after building models on all
434 * available pages, and after setting the rendering parameters.
435 * (8) If the dewa has been serialized, this function is called by
436 * dewarpaRead() when it is read back. It is also called
437 * any time the rendering parameters are changed.
438 * (9) Note: if this has been called with useboth == 1, and useboth
439 * is reset to 0, you should first call dewarpaRestoreModels()
440 * to bring real models from the cache back to the primary array.
441 * </pre>
442 */
443 l_ok
444 dewarpaInsertRefModels(L_DEWARPA *dewa,
445 l_int32 notests,
446 l_int32 debug)
447 {
448 l_int32 i, j, n, val, min, distdown, distup;
449 L_DEWARP *dew;
450 NUMA *na, *nah;
451
452 if (!dewa)
453 return ERROR_INT("dewa not defined", __func__, 1);
454 if (dewa->maxdist < 2)
455 L_INFO("maxdist < 2; no ref models can be used\n", __func__);
456
457 /* Make an indicator numa for pages with valid models. */
458 dewarpaSetValidModels(dewa, notests, debug);
459 n = dewa->maxpage + 1;
460 na = numaMakeConstant(0, n);
461 for (i = 0; i < n; i++) {
462 dew = dewarpaGetDewarp(dewa, i);
463 if (dew && dew->vvalid)
464 numaReplaceNumber(na, i, 1);
465 }
466
467 /* Remove all existing ref models and restore models from cache */
468 dewarpaRestoreModels(dewa);
469
470 /* Move invalid models to the cache, and insert reference dewarps
471 * for pages that need to borrow a model.
472 * First, try to find a valid model for each page. */
473 for (i = 0; i < n; i++) {
474 numaGetIValue(na, i, &val);
475 if (val == 1) continue; /* already has a valid model */
476 if ((dew = dewa->dewarp[i]) != NULL) { /* exists but is not valid; */
477 dewa->dewarpcache[i] = dew; /* move it to the cache */
478 dewa->dewarp[i] = NULL;
479 }
480 if (dewa->maxdist < 2) continue; /* can't use a ref model */
481 /* Look back for nearest model */
482 distdown = distup = dewa->maxdist + 1;
483 for (j = i - 2; j >= 0 && distdown > dewa->maxdist; j -= 2) {
484 numaGetIValue(na, j, &val);
485 if (val == 1) distdown = i - j;
486 }
487 /* Look ahead for nearest model */
488 for (j = i + 2; j < n && distup > dewa->maxdist; j += 2) {
489 numaGetIValue(na, j, &val);
490 if (val == 1) distup = j - i;
491 }
492 min = L_MIN(distdown, distup);
493 if (min > dewa->maxdist) continue; /* no valid model in range */
494 if (distdown <= distup)
495 dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown));
496 else
497 dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup));
498 }
499 numaDestroy(&na);
500
501 /* If a valid model will do, we're finished. */
502 if (dewa->useboth == 0) {
503 dewa->modelsready = 1; /* validated */
504 return 0;
505 }
506
507 /* The request is useboth == 1. Now try to find an hvalid model */
508 nah = numaMakeConstant(0, n);
509 for (i = 0; i < n; i++) {
510 dew = dewarpaGetDewarp(dewa, i);
511 if (dew && dew->hvalid)
512 numaReplaceNumber(nah, i, 1);
513 }
514 for (i = 0; i < n; i++) {
515 numaGetIValue(nah, i, &val);
516 if (val == 1) continue; /* already has a hvalid model */
517 if (dewa->maxdist < 2) continue; /* can't use a ref model */
518 distdown = distup = 100000;
519 for (j = i - 2; j >= 0; j -= 2) { /* look back for nearest model */
520 numaGetIValue(nah, j, &val);
521 if (val == 1) {
522 distdown = i - j;
523 break;
524 }
525 }
526 for (j = i + 2; j < n; j += 2) { /* look ahead for nearest model */
527 numaGetIValue(nah, j, &val);
528 if (val == 1) {
529 distup = j - i;
530 break;
531 }
532 }
533 min = L_MIN(distdown, distup);
534 if (min > dewa->maxdist) continue; /* no hvalid model within range */
535
536 /* We can replace the existing valid model with an hvalid model.
537 * If it's not a reference, save it in the cache. */
538 if ((dew = dewarpaGetDewarp(dewa, i)) == NULL) {
539 L_ERROR("dew is null for page %d!\n", __func__, i);
540 } else {
541 if (dew->hasref == 0) { /* not a ref model */
542 dewa->dewarpcache[i] = dew; /* move it to the cache */
543 dewa->dewarp[i] = NULL; /* must null the ptr */
544 }
545 }
546 if (distdown <= distup) /* insert the hvalid ref model */
547 dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i - distdown));
548 else
549 dewarpaInsertDewarp(dewa, dewarpCreateRef(i, i + distup));
550 }
551 numaDestroy(&nah);
552
553 dewa->modelsready = 1; /* validated */
554 return 0;
555 }
556
557
558 /*!
559 * \brief dewarpaStripRefModels()
560 *
561 * \param[in] dewa populated with dewarp structs for pages
562 * \return 0 if OK, 1 on error
563 *
564 * <pre>
565 * Notes:
566 * (1) This examines each dew in a dewarpa, and removes
567 * all that don't have their own page model (i.e., all
568 * that have "references" to nearby pages with valid models).
569 * These references were generated by dewarpaInsertRefModels(dewa).
570 * </pre>
571 */
572 l_ok
573 dewarpaStripRefModels(L_DEWARPA *dewa)
574 {
575 l_int32 i;
576 L_DEWARP *dew;
577
578 if (!dewa)
579 return ERROR_INT("dewa not defined", __func__, 1);
580
581 for (i = 0; i <= dewa->maxpage; i++) {
582 if ((dew = dewarpaGetDewarp(dewa, i)) != NULL) {
583 if (dew->hasref)
584 dewarpDestroy(&dewa->dewarp[i]);
585 }
586 }
587 dewa->modelsready = 0;
588
589 /* Regenerate the page lists */
590 dewarpaListPages(dewa);
591 return 0;
592 }
593
594
595 /*!
596 * \brief dewarpaRestoreModels()
597 *
598 * \param[in] dewa populated with dewarp structs for pages
599 * \return 0 if OK, 1 on error
600 *
601 * <pre>
602 * Notes:
603 * (1) This puts all real models (and only real models) in the
604 * primary dewarpa array. First remove all dewarps that are
605 * only references to other page models. Then move all models
606 * that had been cached back into the primary dewarp array.
607 * (2) After this is done, we still need to recompute and insert
608 * the reference models before dewa->modelsready is true.
609 * </pre>
610 */
611 l_ok
612 dewarpaRestoreModels(L_DEWARPA *dewa)
613 {
614 l_int32 i;
615 L_DEWARP *dew;
616
617 if (!dewa)
618 return ERROR_INT("dewa not defined", __func__, 1);
619
620 /* Strip out ref models. Then only real models will be in the
621 * primary dewarp array. */
622 dewarpaStripRefModels(dewa);
623
624 /* The cache holds only real models, which are not necessarily valid. */
625 for (i = 0; i <= dewa->maxpage; i++) {
626 if ((dew = dewa->dewarpcache[i]) != NULL) {
627 if (dewa->dewarp[i]) {
628 L_ERROR("dew in both cache and main array!: page %d\n",
629 __func__, i);
630 } else {
631 dewa->dewarp[i] = dew;
632 dewa->dewarpcache[i] = NULL;
633 }
634 }
635 }
636 dewa->modelsready = 0; /* new ref models not yet inserted */
637
638 /* Regenerate the page lists */
639 dewarpaListPages(dewa);
640 return 0;
641 }
642
643
644 /*----------------------------------------------------------------------*
645 * Dewarp debugging output *
646 *----------------------------------------------------------------------*/
647 /*!
648 * \brief dewarpaInfo()
649 *
650 * \param[in] fp
651 * \param[in] dewa
652 * \return 0 if OK, 1 on error
653 */
654 l_ok
655 dewarpaInfo(FILE *fp,
656 L_DEWARPA *dewa)
657 {
658 l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref;
659 L_DEWARP *dew;
660
661 if (!fp)
662 return ERROR_INT("dewa not defined", __func__, 1);
663 if (!dewa)
664 return ERROR_INT("dewa not defined", __func__, 1);
665
666 fprintf(fp, "\nDewarpaInfo: %p\n", dewa);
667 fprintf(fp, "nalloc = %d, maxpage = %d\n", dewa->nalloc, dewa->maxpage);
668 fprintf(fp, "sampling = %d, redfactor = %d, minlines = %d\n",
669 dewa->sampling, dewa->redfactor, dewa->minlines);
670 fprintf(fp, "maxdist = %d, useboth = %d\n",
671 dewa->maxdist, dewa->useboth);
672
673 dewarpaModelStats(dewa, &nnone, &nvsuccess, &nvvalid,
674 &nhsuccess, &nhvalid, &nref);
675 n = numaGetCount(dewa->napages);
676 lept_stderr("Total number of pages with a dew = %d\n", n);
677 lept_stderr("Number of pages without any models = %d\n", nnone);
678 lept_stderr("Number of pages with a vert model = %d\n", nvsuccess);
679 lept_stderr("Number of pages with a valid vert model = %d\n", nvvalid);
680 lept_stderr("Number of pages with both models = %d\n", nhsuccess);
681 lept_stderr("Number of pages with both models valid = %d\n", nhvalid);
682 lept_stderr("Number of pages with a ref model = %d\n", nref);
683
684 for (i = 0; i < n; i++) {
685 numaGetIValue(dewa->napages, i, &pageno);
686 if ((dew = dewarpaGetDewarp(dewa, pageno)) == NULL)
687 continue;
688 lept_stderr("Page: %d\n", dew->pageno);
689 lept_stderr(" hasref = %d, refpage = %d\n",
690 dew->hasref, dew->refpage);
691 lept_stderr(" nlines = %d\n", dew->nlines);
692 lept_stderr(" w = %d, h = %d, nx = %d, ny = %d\n",
693 dew->w, dew->h, dew->nx, dew->ny);
694 if (dew->sampvdispar)
695 lept_stderr(" Vertical disparity builds:\n"
696 " (min,max,abs-diff) line curvature = (%d,%d,%d)\n",
697 dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv);
698 if (dew->samphdispar)
699 lept_stderr(" Horizontal disparity builds:\n"
700 " left edge slope = %d, right edge slope = %d\n"
701 " (left,right,abs-diff) edge curvature = (%d,%d,%d)\n",
702 dew->leftslope, dew->rightslope, dew->leftcurv,
703 dew->rightcurv, L_ABS(dew->leftcurv - dew->rightcurv));
704 }
705 return 0;
706 }
707
708
709 /*!
710 * \brief dewarpaModelStats()
711 *
712 * \param[in] dewa
713 * \param[out] pnnone [optional] number without any model
714 * \param[out] pnvsuccess [optional] number with a vert model
715 * \param[out] pnvvalid [optional] number with a valid vert model
716 * \param[out] pnhsuccess [optional] number with both models
717 * \param[out] pnhvalid [optional] number with both models valid
718 * \param[out] pnref [optional] number with a reference model
719 * \return 0 if OK, 1 on error
720 *
721 * <pre>
722 * Notes:
723 * (1) A page without a model has no dew. It most likely failed to
724 * generate a vertical model, and has not been assigned a ref
725 * model from a neighboring page with a valid vertical model.
726 * (2) A page has vsuccess == 1 if there is at least a model of the
727 * vertical disparity. The model may be invalid, in which case
728 * dewarpaInsertRefModels() will stash it in the cache and
729 * attempt to replace it by a valid ref model.
730 * (3) A vvvalid model is a vertical disparity model whose parameters
731 * satisfy the constraints given in dewarpaSetValidModels().
732 * (4) A page has hsuccess == 1 if both the vertical and horizontal
733 * disparity arrays have been constructed.
734 * (5) An hvalid model has vertical and horizontal disparity
735 * models whose parameters satisfy the constraints given
736 * in dewarpaSetValidModels().
737 * (6) A page has a ref model if it failed to generate a valid
738 * model but was assigned a vvalid or hvalid model on another
739 * page (within maxdist) by dewarpaInsertRefModel().
740 * (7) This calls dewarpaTestForValidModel(); it ignores the vvalid
741 * and hvalid fields.
742 * </pre>
743 */
744 l_ok
745 dewarpaModelStats(L_DEWARPA *dewa,
746 l_int32 *pnnone,
747 l_int32 *pnvsuccess,
748 l_int32 *pnvvalid,
749 l_int32 *pnhsuccess,
750 l_int32 *pnhvalid,
751 l_int32 *pnref)
752 {
753 l_int32 i, n, pageno, nnone, nvsuccess, nvvalid, nhsuccess, nhvalid, nref;
754 L_DEWARP *dew;
755
756 if (!dewa)
757 return ERROR_INT("dewa not defined", __func__, 1);
758
759 dewarpaListPages(dewa);
760 n = numaGetCount(dewa->napages);
761 nnone = nref = nvsuccess = nvvalid = nhsuccess = nhvalid = 0;
762 for (i = 0; i < n; i++) {
763 numaGetIValue(dewa->napages, i, &pageno);
764 dew = dewarpaGetDewarp(dewa, pageno);
765 if (!dew) {
766 nnone++;
767 continue;
768 }
769 if (dew->hasref == 1)
770 nref++;
771 if (dew->vsuccess == 1)
772 nvsuccess++;
773 if (dew->hsuccess == 1)
774 nhsuccess++;
775 dewarpaTestForValidModel(dewa, dew, 0);
776 if (dew->vvalid == 1)
777 nvvalid++;
778 if (dew->hvalid == 1)
779 nhvalid++;
780 }
781
782 if (pnnone) *pnnone = nnone;
783 if (pnref) *pnref = nref;
784 if (pnvsuccess) *pnvsuccess = nvsuccess;
785 if (pnvvalid) *pnvvalid = nvvalid;
786 if (pnhsuccess) *pnhsuccess = nhsuccess;
787 if (pnhvalid) *pnhvalid = nhvalid;
788 return 0;
789 }
790
791
792 /*!
793 * \brief dewarpaTestForValidModel()
794 *
795 * \param[in] dewa
796 * \param[in] dew
797 * \param[in] notests
798 * \return 0 if OK, 1 on error
799 *
800 * <pre>
801 * Notes:
802 * (1) Computes validity of vertical (vvalid) model and both
803 * vertical and horizontal (hvalid) models.
804 * (2) If %notests == 1, this ignores the curvature constraints
805 * and assumes that all successfully built models are valid.
806 * (3) This is just about the models, not the rendering process,
807 * so the value of useboth is not considered here.
808 * </pre>
809 */
810 static l_int32
811 dewarpaTestForValidModel(L_DEWARPA *dewa,
812 L_DEWARP *dew,
813 l_int32 notests)
814 {
815 l_int32 maxcurv, diffcurv, diffedge;
816
817 if (!dewa || !dew)
818 return ERROR_INT("dewa and dew not both defined", __func__, 1);
819
820 if (notests) {
821 dew->vvalid = dew->vsuccess;
822 dew->hvalid = dew->hsuccess;
823 return 0;
824 }
825
826 /* No actual model was built */
827 if (dew->vsuccess == 0) return 0;
828
829 /* Was previously found not to have a valid model */
830 if (dew->hasref == 1) return 0;
831
832 /* vsuccess == 1; a vertical (line) model exists.
833 * First test that the vertical curvatures are within allowed
834 * bounds. Note that all curvatures are signed.*/
835 maxcurv = L_MAX(L_ABS(dew->mincurv), L_ABS(dew->maxcurv));
836 diffcurv = dew->maxcurv - dew->mincurv;
837 if (maxcurv <= dewa->max_linecurv &&
838 diffcurv >= dewa->min_diff_linecurv &&
839 diffcurv <= dewa->max_diff_linecurv) {
840 dew->vvalid = 1;
841 } else {
842 L_INFO("invalid vert model for page %d:\n", __func__, dew->pageno);
843 #if DEBUG_INVALID_MODELS
844 lept_stderr(" max line curv = %d, max allowed = %d\n",
845 maxcurv, dewa->max_linecurv);
846 lept_stderr(" diff line curv = %d, max allowed = %d\n",
847 diffcurv, dewa->max_diff_linecurv);
848 #endif /* DEBUG_INVALID_MODELS */
849 }
850
851 /* If a horizontal (edge) model exists, test for validity. */
852 if (dew->hsuccess) {
853 diffedge = L_ABS(dew->leftcurv - dew->rightcurv);
854 if (L_ABS(dew->leftslope) <= dewa->max_edgeslope &&
855 L_ABS(dew->rightslope) <= dewa->max_edgeslope &&
856 L_ABS(dew->leftcurv) <= dewa->max_edgecurv &&
857 L_ABS(dew->rightcurv) <= dewa->max_edgecurv &&
858 diffedge <= dewa->max_diff_edgecurv) {
859 dew->hvalid = 1;
860 } else {
861 L_INFO("invalid horiz model for page %d:\n", __func__, dew->pageno);
862 #if DEBUG_INVALID_MODELS
863 lept_stderr(" left edge slope = %d, max allowed = %d\n",
864 dew->leftslope, dewa->max_edgeslope);
865 lept_stderr(" right edge slope = %d, max allowed = %d\n",
866 dew->rightslope, dewa->max_edgeslope);
867 lept_stderr(" left edge curv = %d, max allowed = %d\n",
868 dew->leftcurv, dewa->max_edgecurv);
869 lept_stderr(" right edge curv = %d, max allowed = %d\n",
870 dew->rightcurv, dewa->max_edgecurv);
871 lept_stderr(" diff edge curv = %d, max allowed = %d\n",
872 diffedge, dewa->max_diff_edgecurv);
873 #endif /* DEBUG_INVALID_MODELS */
874 }
875 }
876
877 return 0;
878 }
879
880
881 /*!
882 * \brief dewarpaShowArrays()
883 *
884 * \param[in] dewa
885 * \param[in] scalefact on contour images; typ. 0.5
886 * \param[in] first first page model to render
887 * \param[in] last last page model to render; use 0 to go to end
888 * \return 0 if OK, 1 on error
889 *
890 * <pre>
891 * Notes:
892 * (1) Generates a pdf of contour plots of the disparity arrays.
893 * (2) This only shows actual models; not ref models
894 * </pre>
895 */
896 l_ok
897 dewarpaShowArrays(L_DEWARPA *dewa,
898 l_float32 scalefact,
899 l_int32 first,
900 l_int32 last)
901 {
902 char buf[256];
903 l_int32 i, svd, shd;
904 L_BMF *bmf;
905 L_DEWARP *dew;
906 PIX *pixv, *pixvs, *pixh, *pixhs = NULL, *pixt, *pixd;
907 PIXA *pixa;
908
909 if (!dewa)
910 return ERROR_INT("dew not defined", __func__, 1);
911 if (first < 0 || first > dewa->maxpage)
912 return ERROR_INT("first out of bounds", __func__, 1);
913 if (last <= 0 || last > dewa->maxpage) last = dewa->maxpage;
914 if (last < first)
915 return ERROR_INT("last < first", __func__, 1);
916
917 lept_rmdir("lept/dewarp1"); /* temp directory for contour plots */
918 lept_mkdir("lept/dewarp1");
919 if ((bmf = bmfCreate(NULL, 8)) == NULL)
920 L_ERROR("bmf not made; page info not displayed", __func__);
921
922 lept_stderr("Generating contour plots\n");
923 for (i = first; i <= last; i++) {
924 if (i && ((i % 10) == 0))
925 lept_stderr(" .. %d", i);
926 dew = dewarpaGetDewarp(dewa, i);
927 if (!dew) continue;
928 if (dew->hasref == 1) continue;
929 svd = shd = 0;
930 if (dew->sampvdispar) svd = 1;
931 if (dew->samphdispar) shd = 1;
932 if (!svd) {
933 L_ERROR("sampvdispar not made for page %d!\n", __func__, i);
934 continue;
935 }
936
937 /* Generate contour plots at reduced resolution */
938 dewarpPopulateFullRes(dew, NULL, 0, 0);
939 pixv = fpixRenderContours(dew->fullvdispar, 3.0f, 0.15f);
940 pixvs = pixScaleBySampling(pixv, scalefact, scalefact);
941 pixDestroy(&pixv);
942 if (shd) {
943 pixh = fpixRenderContours(dew->fullhdispar, 3.0f, 0.15f);
944 pixhs = pixScaleBySampling(pixh, scalefact, scalefact);
945 pixDestroy(&pixh);
946 }
947 dewarpMinimize(dew);
948
949 /* Save side-by-side */
950 pixa = pixaCreate(2);
951 pixaAddPix(pixa, pixvs, L_INSERT);
952 if (shd)
953 pixaAddPix(pixa, pixhs, L_INSERT);
954 pixt = pixaDisplayTiledInRows(pixa, 32, 1500, 1.0, 0, 30, 2);
955 snprintf(buf, sizeof(buf), "Page %d", i);
956 pixd = pixAddSingleTextblock(pixt, bmf, buf, 0x0000ff00,
957 L_ADD_BELOW, NULL);
958 snprintf(buf, sizeof(buf), "/tmp/lept/dewarp1/arrays_%04d.png", i);
959 pixWriteDebug(buf, pixd, IFF_PNG);
960 pixaDestroy(&pixa);
961 pixDestroy(&pixt);
962 pixDestroy(&pixd);
963 }
964 bmfDestroy(&bmf);
965 lept_stderr("\n");
966
967 lept_stderr("Generating pdf of contour plots\n");
968 convertFilesToPdf("/tmp/lept/dewarp1", "arrays_", 90, 1.0, L_FLATE_ENCODE,
969 0, "Disparity arrays", "/tmp/lept/disparity_arrays.pdf");
970 lept_stderr("Output written to: /tmp/lept/disparity_arrays.pdf\n");
971 return 0;
972 }
973
974
975 /*!
976 * \brief dewarpDebug()
977 *
978 * \param[in] dew
979 * \param[in] subdirs one or more subdirectories of /tmp; e.g., "dew1"
980 * \param[in] index to help label output images; e.g., the page number
981 * \return 0 if OK, 1 on error
982 *
983 * <pre>
984 * Notes:
985 * (1) Prints dewarp fields and generates disparity array contour images.
986 * The contour images are written to file:
987 * /tmp/[subdirs]/pixv_[index].png
988 * </pre>
989 */
990 l_ok
991 dewarpDebug(L_DEWARP *dew,
992 const char *subdirs,
993 l_int32 index)
994 {
995 char fname[256];
996 char *outdir;
997 l_int32 svd, shd;
998 PIX *pixv, *pixh;
999
1000 if (!dew)
1001 return ERROR_INT("dew not defined", __func__, 1);
1002 if (!subdirs)
1003 return ERROR_INT("subdirs not defined", __func__, 1);
1004
1005 lept_stderr("pageno = %d, hasref = %d, refpage = %d\n",
1006 dew->pageno, dew->hasref, dew->refpage);
1007 lept_stderr("sampling = %d, redfactor = %d, minlines = %d\n",
1008 dew->sampling, dew->redfactor, dew->minlines);
1009 svd = shd = 0;
1010 if (!dew->hasref) {
1011 if (dew->sampvdispar) svd = 1;
1012 if (dew->samphdispar) shd = 1;
1013 lept_stderr("sampv = %d, samph = %d\n", svd, shd);
1014 lept_stderr("w = %d, h = %d\n", dew->w, dew->h);
1015 lept_stderr("nx = %d, ny = %d\n", dew->nx, dew->ny);
1016 lept_stderr("nlines = %d\n", dew->nlines);
1017 if (svd) {
1018 lept_stderr("(min,max,abs-diff) line curvature = (%d,%d,%d)\n",
1019 dew->mincurv, dew->maxcurv, dew->maxcurv - dew->mincurv);
1020 }
1021 if (shd) {
1022 lept_stderr("(left edge slope = %d, right edge slope = %d\n",
1023 dew->leftslope, dew->rightslope);
1024 lept_stderr("(left,right,abs-diff) edge curvature = "
1025 "(%d,%d,%d)\n", dew->leftcurv, dew->rightcurv,
1026 L_ABS(dew->leftcurv - dew->rightcurv));
1027 }
1028 }
1029 if (!svd && !shd) {
1030 lept_stderr("No disparity arrays\n");
1031 return 0;
1032 }
1033
1034 dewarpPopulateFullRes(dew, NULL, 0, 0);
1035 lept_mkdir(subdirs);
1036 outdir = pathJoin("/tmp", subdirs);
1037 if (svd) {
1038 pixv = fpixRenderContours(dew->fullvdispar, 3.0f, 0.15f);
1039 snprintf(fname, sizeof(fname), "%s/pixv_%d.png", outdir, index);
1040 pixWriteDebug(fname, pixv, IFF_PNG);
1041 pixDestroy(&pixv);
1042 }
1043 if (shd) {
1044 pixh = fpixRenderContours(dew->fullhdispar, 3.0f, 0.15f);
1045 snprintf(fname, sizeof(fname), "%s/pixh_%d.png", outdir, index);
1046 pixWriteDebug(fname, pixh, IFF_PNG);
1047 pixDestroy(&pixh);
1048 }
1049 LEPT_FREE(outdir);
1050 return 0;
1051 }
1052
1053
1054 /*!
1055 * \brief dewarpShowResults()
1056 *
1057 * \param[in] dewa
1058 * \param[in] sa of indexed input images
1059 * \param[in] boxa crop boxes for input images; can be null
1060 * \param[in] firstpage
1061 * \param[in] lastpage
1062 * \param[in] pdfout filename
1063 * \return 0 if OK, 1 on error
1064 *
1065 * <pre>
1066 * Notes:
1067 * (1) This generates a pdf of image pairs (before, after) for
1068 * the designated set of input pages.
1069 * (2) If the boxa exists, its elements are aligned with numbers
1070 * in the filenames in %sa. It is used to crop the input images.
1071 * It is assumed that the dewa was generated from the cropped
1072 * images. No undercropping is applied before rendering.
1073 * </pre>
1074 */
1075 l_ok
1076 dewarpShowResults(L_DEWARPA *dewa,
1077 SARRAY *sa,
1078 BOXA *boxa,
1079 l_int32 firstpage,
1080 l_int32 lastpage,
1081 const char *pdfout)
1082 {
1083 char bufstr[256];
1084 l_int32 i, modelpage;
1085 L_BMF *bmf;
1086 BOX *box;
1087 L_DEWARP *dew;
1088 PIX *pixs, *pixc, *pixd, *pixt1, *pixt2;
1089 PIXA *pixa;
1090
1091 if (!dewa)
1092 return ERROR_INT("dewa not defined", __func__, 1);
1093 if (!sa)
1094 return ERROR_INT("sa not defined", __func__, 1);
1095 if (!pdfout)
1096 return ERROR_INT("pdfout not defined", __func__, 1);
1097 if (firstpage > lastpage)
1098 return ERROR_INT("invalid first/last page numbers", __func__, 1);
1099
1100 lept_rmdir("lept/dewarp_pdfout");
1101 lept_mkdir("lept/dewarp_pdfout");
1102 bmf = bmfCreate(NULL, 6);
1103
1104 lept_stderr("Dewarping and generating s/by/s view\n");
1105 for (i = firstpage; i <= lastpage; i++) {
1106 if (i && (i % 10 == 0)) lept_stderr(".. %d ", i);
1107 pixs = pixReadIndexed(sa, i);
1108 if (boxa) {
1109 box = boxaGetBox(boxa, i, L_CLONE);
1110 pixc = pixClipRectangle(pixs, box, NULL);
1111 boxDestroy(&box);
1112 }
1113 else
1114 pixc = pixClone(pixs);
1115 dew = dewarpaGetDewarp(dewa, i);
1116 pixd = NULL;
1117 if (dew) {
1118 dewarpaApplyDisparity(dewa, dew->pageno, pixc,
1119 GrayInValue, 0, 0, &pixd, NULL);
1120 dewarpMinimize(dew);
1121 }
1122 pixa = pixaCreate(2);
1123 pixaAddPix(pixa, pixc, L_INSERT);
1124 if (pixd)
1125 pixaAddPix(pixa, pixd, L_INSERT);
1126 pixt1 = pixaDisplayTiledAndScaled(pixa, 32, 500, 2, 0, 35, 2);
1127 if (dew) {
1128 modelpage = (dew->hasref) ? dew->refpage : dew->pageno;
1129 snprintf(bufstr, sizeof(bufstr), "Page %d; using %d\n",
1130 i, modelpage);
1131 }
1132 else
1133 snprintf(bufstr, sizeof(bufstr), "Page %d; no dewarp\n", i);
1134 pixt2 = pixAddSingleTextblock(pixt1, bmf, bufstr, 0x0000ff00,
1135 L_ADD_BELOW, 0);
1136 snprintf(bufstr, sizeof(bufstr), "/tmp/lept/dewarp_pdfout/%05d", i);
1137 pixWriteDebug(bufstr, pixt2, IFF_JFIF_JPEG);
1138 pixaDestroy(&pixa);
1139 pixDestroy(&pixs);
1140 pixDestroy(&pixt1);
1141 pixDestroy(&pixt2);
1142 }
1143 lept_stderr("\n");
1144
1145 lept_stderr("Generating pdf of result\n");
1146 convertFilesToPdf("/tmp/lept/dewarp_pdfout", NULL, 100, 1.0, L_JPEG_ENCODE,
1147 0, "Dewarp sequence", pdfout);
1148 lept_stderr("Output written to: %s\n", pdfout);
1149 bmfDestroy(&bmf);
1150 return 0;
1151 }