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