Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
