Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/projective.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 projective.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Projective (4 pt) image transformation using a sampled | |
| 32 * (to nearest integer) transform on each dest point | |
| 33 * PIX *pixProjectiveSampledPta() | |
| 34 * PIX *pixProjectiveSampled() | |
| 35 * | |
| 36 * Projective (4 pt) image transformation using interpolation | |
| 37 * (or area mapping) for anti-aliasing images that are | |
| 38 * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB | |
| 39 * PIX *pixProjectivePta() | |
| 40 * PIX *pixProjective() | |
| 41 * PIX *pixProjectivePtaColor() | |
| 42 * PIX *pixProjectiveColor() | |
| 43 * PIX *pixProjectivePtaGray() | |
| 44 * PIX *pixProjectiveGray() | |
| 45 * | |
| 46 * Projective transform including alpha (blend) component | |
| 47 * PIX *pixProjectivePtaWithAlpha() | |
| 48 * | |
| 49 * Projective coordinate transformation | |
| 50 * l_int32 getProjectiveXformCoeffs() | |
| 51 * l_int32 projectiveXformSampledPt() | |
| 52 * l_int32 projectiveXformPt() | |
| 53 * | |
| 54 * A projective transform can be specified as a specific functional | |
| 55 * mapping between 4 points in the source and 4 points in the dest. | |
| 56 * It preserves straight lines, but is less stable than a bilinear | |
| 57 * transform, because it contains a division by a quantity that | |
| 58 * can get arbitrarily small.) | |
| 59 * | |
| 60 * We give both a projective coordinate transformation and | |
| 61 * two projective image transformations. | |
| 62 * | |
| 63 * For the former, we ask for the coordinate value (x',y') | |
| 64 * in the transformed space for any point (x,y) in the original | |
| 65 * space. The coefficients of the transformation are found by | |
| 66 * solving 8 simultaneous equations for the 8 coordinates of | |
| 67 * the 4 points in src and dest. The transformation can then | |
| 68 * be used to compute the associated image transform, by | |
| 69 * computing, for each dest pixel, the relevant pixel(s) in | |
| 70 * the source. This can be done either by taking the closest | |
| 71 * src pixel to each transformed dest pixel ("sampling") or | |
| 72 * by doing an interpolation and averaging over 4 source | |
| 73 * pixels with appropriate weightings ("interpolated"). | |
| 74 * | |
| 75 * A typical application would be to remove keystoning | |
| 76 * due to a projective transform in the imaging system. | |
| 77 * | |
| 78 * The projective transform is given by specifying two equations: | |
| 79 * | |
| 80 * x' = (ax + by + c) / (gx + hy + 1) | |
| 81 * y' = (dx + ey + f) / (gx + hy + 1) | |
| 82 * | |
| 83 * where the eight coefficients have been computed from four | |
| 84 * sets of these equations, each for two corresponding data pts. | |
| 85 * In practice, once the coefficients are known, we use the | |
| 86 * equations "backwards": for each point (x,y) in the dest image, | |
| 87 * these two equations are used to compute the corresponding point | |
| 88 * (x',y') in the src. That computed point in the src is then used | |
| 89 * to determine the corresponding dest pixel value in one of two ways: | |
| 90 * | |
| 91 * ~ sampling: simply take the value of the src pixel in which this | |
| 92 * point falls | |
| 93 * ~ interpolation: take appropriate linear combinations of the | |
| 94 * four src pixels that this dest pixel would | |
| 95 * overlap, with the coefficients proportional | |
| 96 * to the amount of overlap | |
| 97 * | |
| 98 * For small warp where there is little scale change, (e.g., | |
| 99 * for rotation) area mapping is nearly equivalent to interpolation. | |
| 100 * | |
| 101 * Typical relative timing of pointwise transforms (sampled = 1.0): | |
| 102 * 8 bpp: sampled 1.0 | |
| 103 * interpolated 1.5 | |
| 104 * 32 bpp: sampled 1.0 | |
| 105 * interpolated 1.6 | |
| 106 * Additionally, the computation time/pixel is nearly the same | |
| 107 * for 8 bpp and 32 bpp, for both sampled and interpolated. | |
| 108 * </pre> | |
| 109 */ | |
| 110 | |
| 111 #ifdef HAVE_CONFIG_H | |
| 112 #include <config_auto.h> | |
| 113 #endif /* HAVE_CONFIG_H */ | |
| 114 | |
| 115 #include <string.h> | |
| 116 #include <math.h> | |
| 117 #include "allheaders.h" | |
| 118 | |
| 119 extern l_float32 AlphaMaskBorderVals[2]; | |
| 120 | |
| 121 /*------------------------------------------------------------n | |
| 122 * Sampled projective image transformation * | |
| 123 *-------------------------------------------------------------*/ | |
| 124 /*! | |
| 125 * \brief pixProjectiveSampledPta() | |
| 126 * | |
| 127 * \param[in] pixs all depths | |
| 128 * \param[in] ptad 4 pts of final coordinate space | |
| 129 * \param[in] ptas 4 pts of initial coordinate space | |
| 130 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 131 * \return pixd, or NULL on error | |
| 132 * | |
| 133 * <pre> | |
| 134 * Notes: | |
| 135 * (1) Brings in either black or white pixels from the boundary. | |
| 136 * (2) Retains colormap, which you can do for a sampled transform.. | |
| 137 * (3) No 3 of the 4 points may be collinear. | |
| 138 * (4) For 8 and 32 bpp pix, better quality is obtained by the | |
| 139 * somewhat slower pixProjectivePta(). See that | |
| 140 * function for relative timings between sampled and interpolated. | |
| 141 * </pre> | |
| 142 */ | |
| 143 PIX * | |
| 144 pixProjectiveSampledPta(PIX *pixs, | |
| 145 PTA *ptad, | |
| 146 PTA *ptas, | |
| 147 l_int32 incolor) | |
| 148 { | |
| 149 l_float32 *vc; | |
| 150 PIX *pixd; | |
| 151 | |
| 152 if (!pixs) | |
| 153 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 154 if (!ptas) | |
| 155 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 156 if (!ptad) | |
| 157 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 158 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 159 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 160 if (ptaGetCount(ptas) != 4) | |
| 161 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL); | |
| 162 if (ptaGetCount(ptad) != 4) | |
| 163 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL); | |
| 164 | |
| 165 /* Get backwards transform from dest to src, and apply it */ | |
| 166 getProjectiveXformCoeffs(ptad, ptas, &vc); | |
| 167 pixd = pixProjectiveSampled(pixs, vc, incolor); | |
| 168 LEPT_FREE(vc); | |
| 169 | |
| 170 return pixd; | |
| 171 } | |
| 172 | |
| 173 | |
| 174 /*! | |
| 175 * \brief pixProjectiveSampled() | |
| 176 * | |
| 177 * \param[in] pixs all depths | |
| 178 * \param[in] vc vector of 8 coefficients for projective transform | |
| 179 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 180 * \return pixd, or NULL on error | |
| 181 * | |
| 182 * <pre> | |
| 183 * Notes: | |
| 184 * (1) Brings in either black or white pixels from the boundary. | |
| 185 * (2) Retains colormap, which you can do for a sampled transform.. | |
| 186 * (3) For 8 or 32 bpp, much better quality is obtained by the | |
| 187 * somewhat slower pixProjective(). See that function | |
| 188 * for relative timings between sampled and interpolated. | |
| 189 * </pre> | |
| 190 */ | |
| 191 PIX * | |
| 192 pixProjectiveSampled(PIX *pixs, | |
| 193 l_float32 *vc, | |
| 194 l_int32 incolor) | |
| 195 { | |
| 196 l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; | |
| 197 l_uint32 val; | |
| 198 l_uint32 *datas, *datad, *lines, *lined; | |
| 199 PIX *pixd; | |
| 200 PIXCMAP *cmap; | |
| 201 | |
| 202 if (!pixs) | |
| 203 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 204 if (!vc) | |
| 205 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 206 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 207 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 208 pixGetDimensions(pixs, &w, &h, &d); | |
| 209 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) | |
| 210 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", __func__, NULL); | |
| 211 | |
| 212 /* Init all dest pixels to color to be brought in from outside */ | |
| 213 pixd = pixCreateTemplate(pixs); | |
| 214 if ((cmap = pixGetColormap(pixs)) != NULL) { | |
| 215 if (incolor == L_BRING_IN_WHITE) | |
| 216 color = 1; | |
| 217 else | |
| 218 color = 0; | |
| 219 pixcmapAddBlackOrWhite(cmap, color, &cmapindex); | |
| 220 pixSetAllArbitrary(pixd, cmapindex); | |
| 221 } else { | |
| 222 if ((d == 1 && incolor == L_BRING_IN_WHITE) || | |
| 223 (d > 1 && incolor == L_BRING_IN_BLACK)) { | |
| 224 pixClearAll(pixd); | |
| 225 } else { | |
| 226 pixSetAll(pixd); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 /* Scan over the dest pixels */ | |
| 231 datas = pixGetData(pixs); | |
| 232 wpls = pixGetWpl(pixs); | |
| 233 datad = pixGetData(pixd); | |
| 234 wpld = pixGetWpl(pixd); | |
| 235 for (i = 0; i < h; i++) { | |
| 236 lined = datad + i * wpld; | |
| 237 for (j = 0; j < w; j++) { | |
| 238 projectiveXformSampledPt(vc, j, i, &x, &y); | |
| 239 if (x < 0 || y < 0 || x >=w || y >= h) | |
| 240 continue; | |
| 241 lines = datas + y * wpls; | |
| 242 if (d == 1) { | |
| 243 val = GET_DATA_BIT(lines, x); | |
| 244 SET_DATA_BIT_VAL(lined, j, val); | |
| 245 } else if (d == 8) { | |
| 246 val = GET_DATA_BYTE(lines, x); | |
| 247 SET_DATA_BYTE(lined, j, val); | |
| 248 } else if (d == 32) { | |
| 249 lined[j] = lines[x]; | |
| 250 } else if (d == 2) { | |
| 251 val = GET_DATA_DIBIT(lines, x); | |
| 252 SET_DATA_DIBIT(lined, j, val); | |
| 253 } else if (d == 4) { | |
| 254 val = GET_DATA_QBIT(lines, x); | |
| 255 SET_DATA_QBIT(lined, j, val); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 return pixd; | |
| 261 } | |
| 262 | |
| 263 | |
| 264 /*---------------------------------------------------------------------* | |
| 265 * Interpolated projective image transformation * | |
| 266 *---------------------------------------------------------------------*/ | |
| 267 /*! | |
| 268 * \brief pixProjectivePta() | |
| 269 * | |
| 270 * \param[in] pixs all depths; colormap ok | |
| 271 * \param[in] ptad 4 pts of final coordinate space | |
| 272 * \param[in] ptas 4 pts of initial coordinate space | |
| 273 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 274 * \return pixd, or NULL on error | |
| 275 * | |
| 276 * <pre> | |
| 277 * Notes: | |
| 278 * (1) Brings in either black or white pixels from the boundary | |
| 279 * (2) Removes any existing colormap, if necessary, before transforming | |
| 280 * </pre> | |
| 281 */ | |
| 282 PIX * | |
| 283 pixProjectivePta(PIX *pixs, | |
| 284 PTA *ptad, | |
| 285 PTA *ptas, | |
| 286 l_int32 incolor) | |
| 287 { | |
| 288 l_int32 d; | |
| 289 l_uint32 colorval; | |
| 290 PIX *pixt1, *pixt2, *pixd; | |
| 291 | |
| 292 if (!pixs) | |
| 293 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 294 if (!ptas) | |
| 295 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 296 if (!ptad) | |
| 297 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 298 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 299 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 300 if (ptaGetCount(ptas) != 4) | |
| 301 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL); | |
| 302 if (ptaGetCount(ptad) != 4) | |
| 303 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL); | |
| 304 | |
| 305 if (pixGetDepth(pixs) == 1) | |
| 306 return pixProjectiveSampledPta(pixs, ptad, ptas, incolor); | |
| 307 | |
| 308 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ | |
| 309 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 310 d = pixGetDepth(pixt1); | |
| 311 if (d < 8) | |
| 312 pixt2 = pixConvertTo8(pixt1, FALSE); | |
| 313 else | |
| 314 pixt2 = pixClone(pixt1); | |
| 315 d = pixGetDepth(pixt2); | |
| 316 | |
| 317 /* Compute actual color to bring in from edges */ | |
| 318 colorval = 0; | |
| 319 if (incolor == L_BRING_IN_WHITE) { | |
| 320 if (d == 8) | |
| 321 colorval = 255; | |
| 322 else /* d == 32 */ | |
| 323 colorval = 0xffffff00; | |
| 324 } | |
| 325 | |
| 326 if (d == 8) | |
| 327 pixd = pixProjectivePtaGray(pixt2, ptad, ptas, colorval); | |
| 328 else /* d == 32 */ | |
| 329 pixd = pixProjectivePtaColor(pixt2, ptad, ptas, colorval); | |
| 330 pixDestroy(&pixt1); | |
| 331 pixDestroy(&pixt2); | |
| 332 return pixd; | |
| 333 } | |
| 334 | |
| 335 | |
| 336 /*! | |
| 337 * \brief pixProjective() | |
| 338 * | |
| 339 * \param[in] pixs all depths; colormap ok | |
| 340 * \param[in] vc vector of 8 coefficients for projective transform | |
| 341 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 342 * \return pixd, or NULL on error | |
| 343 * | |
| 344 * <pre> | |
| 345 * Notes: | |
| 346 * (1) Brings in either black or white pixels from the boundary | |
| 347 * (2) Removes any existing colormap, if necessary, before transforming | |
| 348 * </pre> | |
| 349 */ | |
| 350 PIX * | |
| 351 pixProjective(PIX *pixs, | |
| 352 l_float32 *vc, | |
| 353 l_int32 incolor) | |
| 354 { | |
| 355 l_int32 d; | |
| 356 l_uint32 colorval; | |
| 357 PIX *pixt1, *pixt2, *pixd; | |
| 358 | |
| 359 if (!pixs) | |
| 360 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 361 if (!vc) | |
| 362 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 363 | |
| 364 if (pixGetDepth(pixs) == 1) | |
| 365 return pixProjectiveSampled(pixs, vc, incolor); | |
| 366 | |
| 367 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ | |
| 368 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 369 d = pixGetDepth(pixt1); | |
| 370 if (d < 8) | |
| 371 pixt2 = pixConvertTo8(pixt1, FALSE); | |
| 372 else | |
| 373 pixt2 = pixClone(pixt1); | |
| 374 d = pixGetDepth(pixt2); | |
| 375 | |
| 376 /* Compute actual color to bring in from edges */ | |
| 377 colorval = 0; | |
| 378 if (incolor == L_BRING_IN_WHITE) { | |
| 379 if (d == 8) | |
| 380 colorval = 255; | |
| 381 else /* d == 32 */ | |
| 382 colorval = 0xffffff00; | |
| 383 } | |
| 384 | |
| 385 if (d == 8) | |
| 386 pixd = pixProjectiveGray(pixt2, vc, colorval); | |
| 387 else /* d == 32 */ | |
| 388 pixd = pixProjectiveColor(pixt2, vc, colorval); | |
| 389 pixDestroy(&pixt1); | |
| 390 pixDestroy(&pixt2); | |
| 391 return pixd; | |
| 392 } | |
| 393 | |
| 394 | |
| 395 /*! | |
| 396 * \brief pixProjectivePtaColor() | |
| 397 * | |
| 398 * \param[in] pixs 32 bpp | |
| 399 * \param[in] ptad 4 pts of final coordinate space | |
| 400 * \param[in] ptas 4 pts of initial coordinate space | |
| 401 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE | |
| 402 * \return pixd, or NULL on error | |
| 403 */ | |
| 404 PIX * | |
| 405 pixProjectivePtaColor(PIX *pixs, | |
| 406 PTA *ptad, | |
| 407 PTA *ptas, | |
| 408 l_uint32 colorval) | |
| 409 { | |
| 410 l_float32 *vc; | |
| 411 PIX *pixd; | |
| 412 | |
| 413 if (!pixs) | |
| 414 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 415 if (!ptas) | |
| 416 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 417 if (!ptad) | |
| 418 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 419 if (pixGetDepth(pixs) != 32) | |
| 420 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL); | |
| 421 if (ptaGetCount(ptas) != 4) | |
| 422 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL); | |
| 423 if (ptaGetCount(ptad) != 4) | |
| 424 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL); | |
| 425 | |
| 426 /* Get backwards transform from dest to src, and apply it */ | |
| 427 getProjectiveXformCoeffs(ptad, ptas, &vc); | |
| 428 pixd = pixProjectiveColor(pixs, vc, colorval); | |
| 429 LEPT_FREE(vc); | |
| 430 | |
| 431 return pixd; | |
| 432 } | |
| 433 | |
| 434 | |
| 435 /*! | |
| 436 * \brief pixProjectiveColor() | |
| 437 * | |
| 438 * \param[in] pixs 32 bpp | |
| 439 * \param[in] vc vector of 8 coefficients for projective transform | |
| 440 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE | |
| 441 * \return pixd, or NULL on error | |
| 442 */ | |
| 443 PIX * | |
| 444 pixProjectiveColor(PIX *pixs, | |
| 445 l_float32 *vc, | |
| 446 l_uint32 colorval) | |
| 447 { | |
| 448 l_int32 i, j, w, h, d, wpls, wpld; | |
| 449 l_uint32 val; | |
| 450 l_uint32 *datas, *datad, *lined; | |
| 451 l_float32 x, y; | |
| 452 PIX *pix1, *pix2, *pixd; | |
| 453 | |
| 454 if (!pixs) | |
| 455 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 456 pixGetDimensions(pixs, &w, &h, &d); | |
| 457 if (d != 32) | |
| 458 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL); | |
| 459 if (!vc) | |
| 460 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 461 | |
| 462 datas = pixGetData(pixs); | |
| 463 wpls = pixGetWpl(pixs); | |
| 464 pixd = pixCreateTemplate(pixs); | |
| 465 pixSetAllArbitrary(pixd, colorval); | |
| 466 datad = pixGetData(pixd); | |
| 467 wpld = pixGetWpl(pixd); | |
| 468 | |
| 469 /* Iterate over destination pixels */ | |
| 470 for (i = 0; i < h; i++) { | |
| 471 lined = datad + i * wpld; | |
| 472 for (j = 0; j < w; j++) { | |
| 473 /* Compute float src pixel location corresponding to (i,j) */ | |
| 474 projectiveXformPt(vc, j, i, &x, &y); | |
| 475 linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, | |
| 476 &val); | |
| 477 *(lined + j) = val; | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 /* If rgba, transform the pixs alpha channel and insert in pixd */ | |
| 482 if (pixGetSpp(pixs) == 4) { | |
| 483 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 484 pix2 = pixProjectiveGray(pix1, vc, 255); /* bring in opaque */ | |
| 485 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); | |
| 486 pixDestroy(&pix1); | |
| 487 pixDestroy(&pix2); | |
| 488 } | |
| 489 | |
| 490 return pixd; | |
| 491 } | |
| 492 | |
| 493 | |
| 494 /*! | |
| 495 * \brief pixProjectivePtaGray() | |
| 496 * | |
| 497 * \param[in] pixs 8 bpp | |
| 498 * \param[in] ptad 4 pts of final coordinate space | |
| 499 * \param[in] ptas 4 pts of initial coordinate space | |
| 500 * \param[in] grayval 0 to bring in BLACK, 255 for WHITE | |
| 501 * \return pixd, or NULL on error | |
| 502 */ | |
| 503 PIX * | |
| 504 pixProjectivePtaGray(PIX *pixs, | |
| 505 PTA *ptad, | |
| 506 PTA *ptas, | |
| 507 l_uint8 grayval) | |
| 508 { | |
| 509 l_float32 *vc; | |
| 510 PIX *pixd; | |
| 511 | |
| 512 if (!pixs) | |
| 513 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 514 if (!ptas) | |
| 515 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 516 if (!ptad) | |
| 517 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 518 if (pixGetDepth(pixs) != 8) | |
| 519 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); | |
| 520 if (ptaGetCount(ptas) != 4) | |
| 521 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL); | |
| 522 if (ptaGetCount(ptad) != 4) | |
| 523 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL); | |
| 524 | |
| 525 /* Get backwards transform from dest to src, and apply it */ | |
| 526 getProjectiveXformCoeffs(ptad, ptas, &vc); | |
| 527 pixd = pixProjectiveGray(pixs, vc, grayval); | |
| 528 LEPT_FREE(vc); | |
| 529 | |
| 530 return pixd; | |
| 531 } | |
| 532 | |
| 533 | |
| 534 | |
| 535 /*! | |
| 536 * \brief pixProjectiveGray() | |
| 537 * | |
| 538 * \param[in] pixs 8 bpp | |
| 539 * \param[in] vc vector of 8 coefficients for projective transform | |
| 540 * \param[in] grayval 0 to bring in BLACK, 255 for WHITE | |
| 541 * \return pixd, or NULL on error | |
| 542 */ | |
| 543 PIX * | |
| 544 pixProjectiveGray(PIX *pixs, | |
| 545 l_float32 *vc, | |
| 546 l_uint8 grayval) | |
| 547 { | |
| 548 l_int32 i, j, w, h, wpls, wpld, val; | |
| 549 l_uint32 *datas, *datad, *lined; | |
| 550 l_float32 x, y; | |
| 551 PIX *pixd; | |
| 552 | |
| 553 if (!pixs) | |
| 554 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 555 pixGetDimensions(pixs, &w, &h, NULL); | |
| 556 if (pixGetDepth(pixs) != 8) | |
| 557 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); | |
| 558 if (!vc) | |
| 559 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 560 | |
| 561 datas = pixGetData(pixs); | |
| 562 wpls = pixGetWpl(pixs); | |
| 563 pixd = pixCreateTemplate(pixs); | |
| 564 pixSetAllArbitrary(pixd, grayval); | |
| 565 datad = pixGetData(pixd); | |
| 566 wpld = pixGetWpl(pixd); | |
| 567 | |
| 568 /* Iterate over destination pixels */ | |
| 569 for (i = 0; i < h; i++) { | |
| 570 lined = datad + i * wpld; | |
| 571 for (j = 0; j < w; j++) { | |
| 572 /* Compute float src pixel location corresponding to (i,j) */ | |
| 573 projectiveXformPt(vc, j, i, &x, &y); | |
| 574 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); | |
| 575 SET_DATA_BYTE(lined, j, val); | |
| 576 } | |
| 577 } | |
| 578 | |
| 579 return pixd; | |
| 580 } | |
| 581 | |
| 582 | |
| 583 /*---------------------------------------------------------------------------* | |
| 584 * Projective transform including alpha (blend) component * | |
| 585 *---------------------------------------------------------------------------*/ | |
| 586 /*! | |
| 587 * \brief pixProjectivePtaWithAlpha() | |
| 588 * | |
| 589 * \param[in] pixs 32 bpp rgb | |
| 590 * \param[in] ptad 4 pts of final coordinate space | |
| 591 * \param[in] ptas 4 pts of initial coordinate space | |
| 592 * \param[in] pixg [optional] 8 bpp, for alpha channel, can be null | |
| 593 * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent | |
| 594 * and 1.0 fully opaque | |
| 595 * \param[in] border of pixels added to capture transformed source pixels | |
| 596 * \return pixd, or NULL on error | |
| 597 * | |
| 598 * <pre> | |
| 599 * Notes: | |
| 600 * (1) The alpha channel is transformed separately from pixs, | |
| 601 * and aligns with it, being fully transparent outside the | |
| 602 * boundary of the transformed pixs. For pixels that are fully | |
| 603 * transparent, a blending function like pixBlendWithGrayMask() | |
| 604 * will give zero weight to corresponding pixels in pixs. | |
| 605 * (2) If pixg is NULL, it is generated as an alpha layer that is | |
| 606 * partially opaque, using %fract. Otherwise, it is cropped | |
| 607 * to pixs if required and %fract is ignored. The alpha channel | |
| 608 * in pixs is never used. | |
| 609 * (3) Colormaps are removed. | |
| 610 * (4) When pixs is transformed, it doesn't matter what color is brought | |
| 611 * in because the alpha channel will be transparent (0) there. | |
| 612 * (5) To avoid losing source pixels in the destination, it may be | |
| 613 * necessary to add a border to the source pix before doing | |
| 614 * the projective transformation. This can be any non-negative | |
| 615 * number. | |
| 616 * (6) The input %ptad and %ptas are in a coordinate space before | |
| 617 * the border is added. Internally, we compensate for this | |
| 618 * before doing the projective transform on the image after | |
| 619 * the border is added. | |
| 620 * (7) The default setting for the border values in the alpha channel | |
| 621 * is 0 (transparent) for the outermost ring of pixels and | |
| 622 * (0.5 * fract * 255) for the second ring. When blended over | |
| 623 * a second image, this | |
| 624 * (a) shrinks the visible image to make a clean overlap edge | |
| 625 * with an image below, and | |
| 626 * (b) softens the edges by weakening the aliasing there. | |
| 627 * Use l_setAlphaMaskBorder() to change these values. | |
| 628 * </pre> | |
| 629 */ | |
| 630 PIX * | |
| 631 pixProjectivePtaWithAlpha(PIX *pixs, | |
| 632 PTA *ptad, | |
| 633 PTA *ptas, | |
| 634 PIX *pixg, | |
| 635 l_float32 fract, | |
| 636 l_int32 border) | |
| 637 { | |
| 638 l_int32 ws, hs, d; | |
| 639 PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; | |
| 640 PTA *ptad2, *ptas2; | |
| 641 | |
| 642 if (!pixs) | |
| 643 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 644 pixGetDimensions(pixs, &ws, &hs, &d); | |
| 645 if (d != 32 && pixGetColormap(pixs) == NULL) | |
| 646 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL); | |
| 647 if (pixg && pixGetDepth(pixg) != 8) { | |
| 648 L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", | |
| 649 __func__); | |
| 650 pixg = NULL; | |
| 651 } | |
| 652 if (!pixg && (fract < 0.0 || fract > 1.0)) { | |
| 653 L_WARNING("invalid fract; using 1.0 (fully transparent)\n", __func__); | |
| 654 fract = 1.0; | |
| 655 } | |
| 656 if (!pixg && fract == 0.0) | |
| 657 L_WARNING("fully opaque alpha; image will not be blended\n", __func__); | |
| 658 if (!ptad) | |
| 659 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 660 if (!ptas) | |
| 661 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 662 | |
| 663 /* Add border; the color doesn't matter */ | |
| 664 pixb1 = pixAddBorder(pixs, border, 0); | |
| 665 | |
| 666 /* Transform the ptr arrays to work on the bordered image */ | |
| 667 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); | |
| 668 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); | |
| 669 | |
| 670 /* Do separate projective transform of rgb channels of pixs | |
| 671 * and of pixg */ | |
| 672 pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); | |
| 673 if (!pixg) { | |
| 674 pixg2 = pixCreate(ws, hs, 8); | |
| 675 if (fract == 1.0) | |
| 676 pixSetAll(pixg2); | |
| 677 else | |
| 678 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); | |
| 679 } else { | |
| 680 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); | |
| 681 } | |
| 682 if (ws > 10 && hs > 10) { /* see note 7 */ | |
| 683 pixSetBorderRingVal(pixg2, 1, | |
| 684 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); | |
| 685 pixSetBorderRingVal(pixg2, 2, | |
| 686 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); | |
| 687 | |
| 688 } | |
| 689 pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ | |
| 690 pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); | |
| 691 pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); | |
| 692 pixSetSpp(pixd, 4); | |
| 693 | |
| 694 pixDestroy(&pixg2); | |
| 695 pixDestroy(&pixb1); | |
| 696 pixDestroy(&pixb2); | |
| 697 pixDestroy(&pixga); | |
| 698 ptaDestroy(&ptad2); | |
| 699 ptaDestroy(&ptas2); | |
| 700 return pixd; | |
| 701 } | |
| 702 | |
| 703 | |
| 704 /*-------------------------------------------------------------* | |
| 705 * Projective coordinate transformation * | |
| 706 *-------------------------------------------------------------*/ | |
| 707 /*! | |
| 708 * \brief getProjectiveXformCoeffs() | |
| 709 * | |
| 710 * \param[in] ptas source 4 points; unprimed | |
| 711 * \param[in] ptad transformed 4 points; primed | |
| 712 * \param[out] pvc vector of coefficients of transform | |
| 713 * \return 0 if OK; 1 on error | |
| 714 * | |
| 715 * We have a set of 8 equations, describing the projective | |
| 716 * transformation that takes 4 points ptas into 4 other | |
| 717 * points ptad. These equations are: | |
| 718 * | |
| 719 * x1' = c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1 | |
| 720 * y1' = c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1 | |
| 721 * x2' = c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1 | |
| 722 * y2' = c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1 | |
| 723 * x3' = c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1 | |
| 724 * y3' = c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1 | |
| 725 * x4' = c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1 | |
| 726 * y4' = c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1 | |
| 727 * | |
| 728 * Multiplying both sides of each eqn by the denominator, we get | |
| 729 * | |
| 730 * AC = B | |
| 731 * | |
| 732 * where B and C are column vectors | |
| 733 * | |
| 734 * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ] | |
| 735 * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ] | |
| 736 * | |
| 737 * and A is the 8x8 matrix | |
| 738 * | |
| 739 * x1 y1 1 0 0 0 -x1*x1' -y1*x1' | |
| 740 * 0 0 0 x1 y1 1 -x1*y1' -y1*y1' | |
| 741 * x2 y2 1 0 0 0 -x2*x2' -y2*x2' | |
| 742 * 0 0 0 x2 y2 1 -x2*y2' -y2*y2' | |
| 743 * x3 y3 1 0 0 0 -x3*x3' -y3*x3' | |
| 744 * 0 0 0 x3 y3 1 -x3*y3' -y3*y3' | |
| 745 * x4 y4 1 0 0 0 -x4*x4' -y4*x4' | |
| 746 * 0 0 0 x4 y4 1 -x4*y4' -y4*y4' | |
| 747 * | |
| 748 * These eight equations are solved here for the coefficients C. | |
| 749 * | |
| 750 * These eight coefficients can then be used to find the mapping | |
| 751 * x,y) --> (x',y': | |
| 752 * | |
| 753 * x' = c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1 | |
| 754 * y' = c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1 | |
| 755 * | |
| 756 * that is implemented in projectiveXformSampled and | |
| 757 * projectiveXFormInterpolated. | |
| 758 */ | |
| 759 l_ok | |
| 760 getProjectiveXformCoeffs(PTA *ptas, | |
| 761 PTA *ptad, | |
| 762 l_float32 **pvc) | |
| 763 { | |
| 764 l_int32 i; | |
| 765 l_float32 x1, y1, x2, y2, x3, y3, x4, y4; | |
| 766 l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ | |
| 767 l_float32 *a[8]; /* 8x8 matrix A */ | |
| 768 | |
| 769 if (!ptas) | |
| 770 return ERROR_INT("ptas not defined", __func__, 1); | |
| 771 if (!ptad) | |
| 772 return ERROR_INT("ptad not defined", __func__, 1); | |
| 773 if (!pvc) | |
| 774 return ERROR_INT("&vc not defined", __func__, 1); | |
| 775 | |
| 776 b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); | |
| 777 *pvc = b; | |
| 778 ptaGetPt(ptas, 0, &x1, &y1); | |
| 779 ptaGetPt(ptas, 1, &x2, &y2); | |
| 780 ptaGetPt(ptas, 2, &x3, &y3); | |
| 781 ptaGetPt(ptas, 3, &x4, &y4); | |
| 782 ptaGetPt(ptad, 0, &b[0], &b[1]); | |
| 783 ptaGetPt(ptad, 1, &b[2], &b[3]); | |
| 784 ptaGetPt(ptad, 2, &b[4], &b[5]); | |
| 785 ptaGetPt(ptad, 3, &b[6], &b[7]); | |
| 786 | |
| 787 for (i = 0; i < 8; i++) | |
| 788 a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32)); | |
| 789 a[0][0] = x1; | |
| 790 a[0][1] = y1; | |
| 791 a[0][2] = 1.; | |
| 792 a[0][6] = -x1 * b[0]; | |
| 793 a[0][7] = -y1 * b[0]; | |
| 794 a[1][3] = x1; | |
| 795 a[1][4] = y1; | |
| 796 a[1][5] = 1; | |
| 797 a[1][6] = -x1 * b[1]; | |
| 798 a[1][7] = -y1 * b[1]; | |
| 799 a[2][0] = x2; | |
| 800 a[2][1] = y2; | |
| 801 a[2][2] = 1.; | |
| 802 a[2][6] = -x2 * b[2]; | |
| 803 a[2][7] = -y2 * b[2]; | |
| 804 a[3][3] = x2; | |
| 805 a[3][4] = y2; | |
| 806 a[3][5] = 1; | |
| 807 a[3][6] = -x2 * b[3]; | |
| 808 a[3][7] = -y2 * b[3]; | |
| 809 a[4][0] = x3; | |
| 810 a[4][1] = y3; | |
| 811 a[4][2] = 1.; | |
| 812 a[4][6] = -x3 * b[4]; | |
| 813 a[4][7] = -y3 * b[4]; | |
| 814 a[5][3] = x3; | |
| 815 a[5][4] = y3; | |
| 816 a[5][5] = 1; | |
| 817 a[5][6] = -x3 * b[5]; | |
| 818 a[5][7] = -y3 * b[5]; | |
| 819 a[6][0] = x4; | |
| 820 a[6][1] = y4; | |
| 821 a[6][2] = 1.; | |
| 822 a[6][6] = -x4 * b[6]; | |
| 823 a[6][7] = -y4 * b[6]; | |
| 824 a[7][3] = x4; | |
| 825 a[7][4] = y4; | |
| 826 a[7][5] = 1; | |
| 827 a[7][6] = -x4 * b[7]; | |
| 828 a[7][7] = -y4 * b[7]; | |
| 829 | |
| 830 gaussjordan(a, b, 8); | |
| 831 | |
| 832 for (i = 0; i < 8; i++) | |
| 833 LEPT_FREE(a[i]); | |
| 834 | |
| 835 return 0; | |
| 836 } | |
| 837 | |
| 838 | |
| 839 /*! | |
| 840 * \brief projectiveXformSampledPt() | |
| 841 * | |
| 842 * \param[in] vc vector of 8 coefficients | |
| 843 * \param[in] x, y initial point | |
| 844 * \param[out] pxp, pyp transformed point | |
| 845 * \return 0 if OK; 1 on error | |
| 846 * | |
| 847 * <pre> | |
| 848 * Notes: | |
| 849 * (1) This finds the nearest pixel coordinates of the transformed point. | |
| 850 * (2) It does not check ptrs for returned data! | |
| 851 * </pre> | |
| 852 */ | |
| 853 l_ok | |
| 854 projectiveXformSampledPt(l_float32 *vc, | |
| 855 l_int32 x, | |
| 856 l_int32 y, | |
| 857 l_int32 *pxp, | |
| 858 l_int32 *pyp) | |
| 859 { | |
| 860 l_float32 factor; | |
| 861 l_float64 denom; | |
| 862 | |
| 863 if (!vc) | |
| 864 return ERROR_INT("vc not defined", __func__, 1); | |
| 865 | |
| 866 if ((denom = vc[6] * x + vc[7] * y + 1.0f) == 0.0f) | |
| 867 return ERROR_INT("denom = 0.0", __func__, 1); | |
| 868 factor = 1.0f / denom; | |
| 869 *pxp = (l_int32)(factor * (vc[0] * x + vc[1] * y + vc[2]) + 0.5f); | |
| 870 *pyp = (l_int32)(factor * (vc[3] * x + vc[4] * y + vc[5]) + 0.5f); | |
| 871 return 0; | |
| 872 } | |
| 873 | |
| 874 | |
| 875 /*! | |
| 876 * \brief projectiveXformPt() | |
| 877 * | |
| 878 * \param[in] vc vector of 8 coefficients | |
| 879 * \param[in] x, y initial point | |
| 880 * \param[out] pxp, pyp transformed point | |
| 881 * \return 0 if OK; 1 on error | |
| 882 * | |
| 883 * <pre> | |
| 884 * Notes: | |
| 885 * (1) This computes the floating point location of the transformed point. | |
| 886 * (2) It does not check ptrs for returned data! | |
| 887 * </pre> | |
| 888 */ | |
| 889 l_ok | |
| 890 projectiveXformPt(l_float32 *vc, | |
| 891 l_int32 x, | |
| 892 l_int32 y, | |
| 893 l_float32 *pxp, | |
| 894 l_float32 *pyp) | |
| 895 { | |
| 896 l_float32 factor; | |
| 897 l_float64 denom; | |
| 898 | |
| 899 if (!vc) | |
| 900 return ERROR_INT("vc not defined", __func__, 1); | |
| 901 | |
| 902 if ((denom = vc[6] * x + vc[7] * y + 1.0f) == 0.0f) | |
| 903 return ERROR_INT("denom = 0.0", __func__, 1); | |
| 904 factor = 1.0f / denom; | |
| 905 *pxp = factor * (vc[0] * x + vc[1] * y + vc[2]); | |
| 906 *pyp = factor * (vc[3] * x + vc[4] * y + vc[5]); | |
| 907 return 0; | |
| 908 } |
