Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/affine.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 /*! | |
| 29 * \file affine.c | |
| 30 * <pre> | |
| 31 * | |
| 32 * Affine (3 pt) image transformation using a sampled | |
| 33 * (to nearest integer) transform on each dest point | |
| 34 * PIX *pixAffineSampledPta() | |
| 35 * PIX *pixAffineSampled() | |
| 36 * | |
| 37 * Affine (3 pt) image transformation using interpolation | |
| 38 * (or area mapping) for anti-aliasing images that are | |
| 39 * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB | |
| 40 * PIX *pixAffinePta() | |
| 41 * PIX *pixAffine() | |
| 42 * PIX *pixAffinePtaColor() | |
| 43 * PIX *pixAffineColor() | |
| 44 * PIX *pixAffinePtaGray() | |
| 45 * PIX *pixAffineGray() | |
| 46 * | |
| 47 * Affine transform including alpha (blend) component | |
| 48 * PIX *pixAffinePtaWithAlpha() | |
| 49 * | |
| 50 * Affine coordinate transformation | |
| 51 * l_int32 getAffineXformCoeffs() | |
| 52 * l_int32 affineInvertXform() | |
| 53 * l_int32 affineXformSampledPt() | |
| 54 * l_int32 affineXformPt() | |
| 55 * | |
| 56 * Interpolation helper functions | |
| 57 * l_int32 linearInterpolatePixelGray() | |
| 58 * l_int32 linearInterpolatePixelColor() | |
| 59 * | |
| 60 * Gauss-jordan linear equation solver | |
| 61 * l_int32 gaussjordan() | |
| 62 * | |
| 63 * Affine image transformation using a sequence of | |
| 64 * shear/scale/translation operations | |
| 65 * PIX *pixAffineSequential() | |
| 66 * | |
| 67 * One can define a coordinate space by the location of the origin, | |
| 68 * the orientation of x and y axes, and the unit scaling along | |
| 69 * each axis. An affine transform is a general linear | |
| 70 * transformation from one coordinate space to another. | |
| 71 * | |
| 72 * For the general case, we can define the affine transform using | |
| 73 * two sets of three (noncollinear) points in a plane. One set | |
| 74 * corresponds to the input (src) coordinate space; the other to the | |
| 75 * transformed (dest) coordinate space. Each point in the | |
| 76 * src corresponds to one of the points in the dest. With two | |
| 77 * sets of three points, we get a set of 6 equations in 6 unknowns | |
| 78 * that specifies the mapping between the coordinate spaces. | |
| 79 * The interface here allows you to specify either the corresponding | |
| 80 * sets of 3 points, or the transform itself (as a vector of 6 | |
| 81 * coefficients). | |
| 82 * | |
| 83 * Given the transform as a vector of 6 coefficients, we can compute | |
| 84 * both a a pointwise affine coordinate transformation and an | |
| 85 * affine image transformation. | |
| 86 * | |
| 87 * To compute the coordinate transform, we need the coordinate | |
| 88 * value (x',y') in the transformed space for any point (x,y) | |
| 89 * in the original space. To derive this transform from the | |
| 90 * three corresponding points, it is convenient to express the affine | |
| 91 * coordinate transformation using an LU decomposition of | |
| 92 * a set of six linear equations that express the six coordinates | |
| 93 * of the three points in the transformed space as a function of | |
| 94 * the six coordinates in the original space. Once we have | |
| 95 * this transform matrix , we can transform an image by | |
| 96 * finding, for each destination pixel, the pixel (or pixels) | |
| 97 * in the source that give rise to it. | |
| 98 * | |
| 99 * This 'pointwise' transformation can be done either by sampling | |
| 100 * and picking a single pixel in the src to replicate into the dest, | |
| 101 * or by interpolating (or averaging) over four src pixels to | |
| 102 * determine the value of the dest pixel. The first method is | |
| 103 * implemented by pixAffineSampled() and the second method by | |
| 104 * pixAffine(). The interpolated method can only be used for | |
| 105 * images with more than 1 bpp, but for these, the image quality | |
| 106 * is significantly better than the sampled method, due to | |
| 107 * the 'antialiasing' effect of weighting the src pixels. | |
| 108 * | |
| 109 * Interpolation works well when there is relatively little scaling, | |
| 110 * or if there is image expansion in general. However, if there | |
| 111 * is significant image reduction, one should apply a low-pass | |
| 112 * filter before subsampling to avoid aliasing the high frequencies. | |
| 113 * | |
| 114 * A typical application might be to align two images, which | |
| 115 * may be scaled, rotated and translated versions of each other. | |
| 116 * Through some pre-processing, three corresponding points are | |
| 117 * located in each of the two images. One of the images is | |
| 118 * then to be (affine) transformed to align with the other. | |
| 119 * As mentioned, the standard way to do this is to use three | |
| 120 * sets of points, compute the 6 transformation coefficients | |
| 121 * from these points that describe the linear transformation, | |
| 122 * | |
| 123 * x' = ax + by + c | |
| 124 * y' = dx + ey + f | |
| 125 * | |
| 126 * and use this in a pointwise manner to transform the image. | |
| 127 * | |
| 128 * N.B. Be sure to see the comment in getAffineXformCoeffs(), | |
| 129 * regarding using the inverse of the affine transform for points | |
| 130 * to transform images. | |
| 131 * | |
| 132 * There is another way to do this transformation; namely, | |
| 133 * by doing a sequence of simple affine transforms, without | |
| 134 * computing directly the affine coordinate transformation. | |
| 135 * We have at our disposal (1) translations (using rasterop), | |
| 136 * (2) horizontal and vertical shear about any horizontal and vertical | |
| 137 * line, respectively, and (3) non-isotropic scaling by two | |
| 138 * arbitrary x and y scaling factors. We also have rotation | |
| 139 * about an arbitrary point, but this is equivalent to a set | |
| 140 * of three shears so we do not need to use it. | |
| 141 * | |
| 142 * Why might we do this? For binary images, it is usually | |
| 143 * more efficient to do such transformations by a sequence | |
| 144 * of word parallel operations. Shear and translation can be | |
| 145 * done in-place and word parallel; arbitrary scaling is | |
| 146 * mostly pixel-wise. | |
| 147 * | |
| 148 * Suppose that we are transforming image 1 to correspond to image 2. | |
| 149 * We have a set of three points, describing the coordinate space | |
| 150 * embedded in image 1, and we need to transform image 1 until | |
| 151 * those three points exactly correspond to the new coordinate space | |
| 152 * defined by the second set of three points. In our image | |
| 153 * matching application, the latter set of three points was | |
| 154 * found to be the corresponding points in image 2. | |
| 155 * | |
| 156 * The most elegant way I can think of to do such a sequential | |
| 157 * implementation is to imagine that we're going to transform | |
| 158 * BOTH images until they're aligned. (We don't really want | |
| 159 * to transform both, because in fact we may only have one image | |
| 160 * that is undergoing a general affine transformation.) | |
| 161 * | |
| 162 * Choose the 3 corresponding points as follows: | |
| 163 * ~ The 1st point is an origin | |
| 164 * ~ The 2nd point gives the orientation and scaling of the | |
| 165 * "x" axis with respect to the origin | |
| 166 * ~ The 3rd point does likewise for the "y" axis. | |
| 167 * These "axes" must not be collinear; otherwise they are | |
| 168 * arbitrary (although some strange things will happen if | |
| 169 * the handedness sweeping through the minimum angle between | |
| 170 * the axes is opposite). | |
| 171 * | |
| 172 * An important constraint is that we have shear operations | |
| 173 * about an arbitrary horizontal or vertical line, but always | |
| 174 * parallel to the x or y axis. If we continue to pretend that | |
| 175 * we have an unprimed coordinate space embedded in image 1 and | |
| 176 * a primed coordinate space embedded in image 2, we imagine | |
| 177 * (a) transforming image 1 by horizontal and vertical shears about | |
| 178 * point 1 to align points 3 and 2 along the y and x axes, | |
| 179 * respectively, and (b) transforming image 2 by horizontal and | |
| 180 * vertical shears about point 1' to align points 3' and 2' along | |
| 181 * the y and x axes. Then we scale image 1 so that the distances | |
| 182 * from 1 to 2 and from 1 to 3 are equal to the distances in | |
| 183 * image 2 from 1' to 2' and from 1' to 3'. This scaling operation | |
| 184 * leaves the true image origin, at (0,0) invariant, and will in | |
| 185 * general translate point 1. The original points 1 and 1' will | |
| 186 * typically not coincide in any event, so we must translate | |
| 187 * the origin of image 1, at its current point 1, to the origin | |
| 188 * of image 2 at 1'. The images should now be aligned. But | |
| 189 * because we never really transformed image 2 (and image 2 may | |
| 190 * not even exist), we now perform on image 1 the reverse of | |
| 191 * the shear transforms that we imagined doing on image 2; | |
| 192 * namely, the negative vertical shear followed by the negative | |
| 193 * horizontal shear. Image 1 should now have its transformed | |
| 194 * unprimed coordinates aligned with the original primed | |
| 195 * coordinates. In all this, it is only necessary to keep track | |
| 196 * of the shear angles and translations of points during the shears. | |
| 197 * What has been accomplished is a general affine transformation | |
| 198 * on image 1. | |
| 199 * | |
| 200 * Having described all this, if you are going to use an | |
| 201 * affine transformation in an application, this is what you | |
| 202 * need to know: | |
| 203 * | |
| 204 * (1) You should NEVER use the sequential method, because | |
| 205 * the image quality for 1 bpp text is much poorer | |
| 206 * (even though it is about 2x faster than the pointwise sampled | |
| 207 * method), and for images with depth greater than 1, it is | |
| 208 * nearly 20x slower than the pointwise sampled method | |
| 209 * and over 10x slower than the pointwise interpolated method! | |
| 210 * The sequential method is given here for purely | |
| 211 * pedagogical reasons. | |
| 212 * | |
| 213 * (2) For 1 bpp images, use the pointwise sampled function | |
| 214 * pixAffineSampled(). For all other images, the best | |
| 215 * quality results result from using the pointwise | |
| 216 * interpolated function pixAffinePta() or pixAffine(); | |
| 217 * the cost is less than a doubling of the computation time | |
| 218 * with respect to the sampled function. If you use | |
| 219 * interpolation on colormapped images, the colormap will | |
| 220 * be removed, resulting in either a grayscale or color | |
| 221 * image, depending on the values in the colormap. | |
| 222 * If you want to retain the colormap, use pixAffineSampled(). | |
| 223 * | |
| 224 * Typical relative timing of pointwise transforms (sampled = 1.0): | |
| 225 * 8 bpp: sampled 1.0 | |
| 226 * interpolated 1.6 | |
| 227 * 32 bpp: sampled 1.0 | |
| 228 * interpolated 1.8 | |
| 229 * Additionally, the computation time/pixel is nearly the same | |
| 230 * for 8 bpp and 32 bpp, for both sampled and interpolated. | |
| 231 * </pre> | |
| 232 */ | |
| 233 | |
| 234 #ifdef HAVE_CONFIG_H | |
| 235 #include <config_auto.h> | |
| 236 #endif /* HAVE_CONFIG_H */ | |
| 237 | |
| 238 #include <string.h> | |
| 239 #include <math.h> | |
| 240 #include "allheaders.h" | |
| 241 | |
| 242 extern l_float32 AlphaMaskBorderVals[2]; | |
| 243 | |
| 244 #ifndef NO_CONSOLE_IO | |
| 245 #define DEBUG 0 | |
| 246 #endif /* ~NO_CONSOLE_IO */ | |
| 247 | |
| 248 /*-------------------------------------------------------------* | |
| 249 * Sampled affine image transformation * | |
| 250 *-------------------------------------------------------------*/ | |
| 251 /*! | |
| 252 * \brief pixAffineSampledPta() | |
| 253 * | |
| 254 * \param[in] pixs all depths | |
| 255 * \param[in] ptad 3 pts of final coordinate space | |
| 256 * \param[in] ptas 3 pts of initial coordinate space | |
| 257 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 258 * \return pixd, or NULL on error | |
| 259 * | |
| 260 * <pre> | |
| 261 * Notes: | |
| 262 * (1) Brings in either black or white pixels from the boundary. | |
| 263 * (2) Retains colormap, which you can do for a sampled transform.. | |
| 264 * (3) The 3 points must not be collinear. | |
| 265 * (4) The order of the 3 points is arbitrary; however, to compare | |
| 266 * with the sequential transform they must be in these locations | |
| 267 * and in this order: origin, x-axis, y-axis. | |
| 268 * (5) For 1 bpp images, this has much better quality results | |
| 269 * than pixAffineSequential(), particularly for text. | |
| 270 * It is about 3x slower, but does not require additional | |
| 271 * border pixels. The poor quality of pixAffineSequential() | |
| 272 * is due to repeated quantized transforms. It is strongly | |
| 273 * recommended that pixAffineSampled() be used for 1 bpp images. | |
| 274 * (6) For 8 or 32 bpp, much better quality is obtained by the | |
| 275 * somewhat slower pixAffinePta(). See that function | |
| 276 * for relative timings between sampled and interpolated. | |
| 277 * (7) To repeat, use of the sequential transform, | |
| 278 * pixAffineSequential(), for any images, is discouraged. | |
| 279 * </pre> | |
| 280 */ | |
| 281 PIX * | |
| 282 pixAffineSampledPta(PIX *pixs, | |
| 283 PTA *ptad, | |
| 284 PTA *ptas, | |
| 285 l_int32 incolor) | |
| 286 { | |
| 287 l_float32 *vc; | |
| 288 PIX *pixd; | |
| 289 | |
| 290 if (!pixs) | |
| 291 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 292 if (!ptas) | |
| 293 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 294 if (!ptad) | |
| 295 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 296 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 297 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 298 if (ptaGetCount(ptas) != 3) | |
| 299 return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL); | |
| 300 if (ptaGetCount(ptad) != 3) | |
| 301 return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL); | |
| 302 | |
| 303 /* Get backwards transform from dest to src, and apply it */ | |
| 304 getAffineXformCoeffs(ptad, ptas, &vc); | |
| 305 pixd = pixAffineSampled(pixs, vc, incolor); | |
| 306 LEPT_FREE(vc); | |
| 307 | |
| 308 return pixd; | |
| 309 } | |
| 310 | |
| 311 | |
| 312 /*! | |
| 313 * \brief pixAffineSampled() | |
| 314 * | |
| 315 * \param[in] pixs all depths | |
| 316 * \param[in] vc vector of 6 coefficients for affine transformation | |
| 317 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 318 * \return pixd, or NULL on error | |
| 319 * | |
| 320 * <pre> | |
| 321 * Notes: | |
| 322 * (1) Brings in either black or white pixels from the boundary. | |
| 323 * (2) Retains colormap, which you can do for a sampled transform.. | |
| 324 * (3) For 8 or 32 bpp, much better quality is obtained by the | |
| 325 * somewhat slower pixAffine(). See that function | |
| 326 * for relative timings between sampled and interpolated. | |
| 327 * </pre> | |
| 328 */ | |
| 329 PIX * | |
| 330 pixAffineSampled(PIX *pixs, | |
| 331 l_float32 *vc, | |
| 332 l_int32 incolor) | |
| 333 { | |
| 334 l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; | |
| 335 l_uint32 val; | |
| 336 l_uint32 *datas, *datad, *lines, *lined; | |
| 337 PIX *pixd; | |
| 338 PIXCMAP *cmap; | |
| 339 | |
| 340 if (!pixs) | |
| 341 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 342 if (!vc) | |
| 343 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 344 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 345 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 346 pixGetDimensions(pixs, &w, &h, &d); | |
| 347 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) | |
| 348 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", __func__, NULL); | |
| 349 | |
| 350 /* Init all dest pixels to color to be brought in from outside */ | |
| 351 pixd = pixCreateTemplate(pixs); | |
| 352 if ((cmap = pixGetColormap(pixs)) != NULL) { | |
| 353 if (incolor == L_BRING_IN_WHITE) | |
| 354 color = 1; | |
| 355 else | |
| 356 color = 0; | |
| 357 pixcmapAddBlackOrWhite(cmap, color, &cmapindex); | |
| 358 pixSetAllArbitrary(pixd, cmapindex); | |
| 359 } else { | |
| 360 if ((d == 1 && incolor == L_BRING_IN_WHITE) || | |
| 361 (d > 1 && incolor == L_BRING_IN_BLACK)) { | |
| 362 pixClearAll(pixd); | |
| 363 } else { | |
| 364 pixSetAll(pixd); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 /* Scan over the dest pixels */ | |
| 369 datas = pixGetData(pixs); | |
| 370 wpls = pixGetWpl(pixs); | |
| 371 datad = pixGetData(pixd); | |
| 372 wpld = pixGetWpl(pixd); | |
| 373 for (i = 0; i < h; i++) { | |
| 374 lined = datad + i * wpld; | |
| 375 for (j = 0; j < w; j++) { | |
| 376 affineXformSampledPt(vc, j, i, &x, &y); | |
| 377 if (x < 0 || y < 0 || x >=w || y >= h) | |
| 378 continue; | |
| 379 lines = datas + y * wpls; | |
| 380 if (d == 1) { | |
| 381 val = GET_DATA_BIT(lines, x); | |
| 382 SET_DATA_BIT_VAL(lined, j, val); | |
| 383 } else if (d == 8) { | |
| 384 val = GET_DATA_BYTE(lines, x); | |
| 385 SET_DATA_BYTE(lined, j, val); | |
| 386 } else if (d == 32) { | |
| 387 lined[j] = lines[x]; | |
| 388 } else if (d == 2) { | |
| 389 val = GET_DATA_DIBIT(lines, x); | |
| 390 SET_DATA_DIBIT(lined, j, val); | |
| 391 } else if (d == 4) { | |
| 392 val = GET_DATA_QBIT(lines, x); | |
| 393 SET_DATA_QBIT(lined, j, val); | |
| 394 } | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 return pixd; | |
| 399 } | |
| 400 | |
| 401 | |
| 402 /*---------------------------------------------------------------------* | |
| 403 * Interpolated affine image transformation * | |
| 404 *---------------------------------------------------------------------*/ | |
| 405 /*! | |
| 406 * \brief pixAffinePta() | |
| 407 * | |
| 408 * \param[in] pixs all depths; colormap ok | |
| 409 * \param[in] ptad 3 pts of final coordinate space | |
| 410 * \param[in] ptas 3 pts of initial coordinate space | |
| 411 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 412 * \return pixd, or NULL on error | |
| 413 * | |
| 414 * <pre> | |
| 415 * Notes: | |
| 416 * (1) Brings in either black or white pixels from the boundary | |
| 417 * (2) Removes any existing colormap, if necessary, before transforming | |
| 418 * </pre> | |
| 419 */ | |
| 420 PIX * | |
| 421 pixAffinePta(PIX *pixs, | |
| 422 PTA *ptad, | |
| 423 PTA *ptas, | |
| 424 l_int32 incolor) | |
| 425 { | |
| 426 l_int32 d; | |
| 427 l_uint32 colorval; | |
| 428 PIX *pixt1, *pixt2, *pixd; | |
| 429 | |
| 430 if (!pixs) | |
| 431 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 432 if (!ptas) | |
| 433 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 434 if (!ptad) | |
| 435 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 436 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 437 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 438 if (ptaGetCount(ptas) != 3) | |
| 439 return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL); | |
| 440 if (ptaGetCount(ptad) != 3) | |
| 441 return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL); | |
| 442 | |
| 443 if (pixGetDepth(pixs) == 1) | |
| 444 return pixAffineSampledPta(pixs, ptad, ptas, incolor); | |
| 445 | |
| 446 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ | |
| 447 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 448 d = pixGetDepth(pixt1); | |
| 449 if (d < 8) | |
| 450 pixt2 = pixConvertTo8(pixt1, FALSE); | |
| 451 else | |
| 452 pixt2 = pixClone(pixt1); | |
| 453 d = pixGetDepth(pixt2); | |
| 454 | |
| 455 /* Compute actual color to bring in from edges */ | |
| 456 colorval = 0; | |
| 457 if (incolor == L_BRING_IN_WHITE) { | |
| 458 if (d == 8) | |
| 459 colorval = 255; | |
| 460 else /* d == 32 */ | |
| 461 colorval = 0xffffff00; | |
| 462 } | |
| 463 | |
| 464 if (d == 8) | |
| 465 pixd = pixAffinePtaGray(pixt2, ptad, ptas, colorval); | |
| 466 else /* d == 32 */ | |
| 467 pixd = pixAffinePtaColor(pixt2, ptad, ptas, colorval); | |
| 468 pixDestroy(&pixt1); | |
| 469 pixDestroy(&pixt2); | |
| 470 return pixd; | |
| 471 } | |
| 472 | |
| 473 | |
| 474 /*! | |
| 475 * \brief pixAffine() | |
| 476 * | |
| 477 * \param[in] pixs all depths; colormap ok | |
| 478 * \param[in] vc vector of 6 coefficients for affine transformation | |
| 479 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK | |
| 480 * \return pixd, or NULL on error | |
| 481 * | |
| 482 * <pre> | |
| 483 * Notes: | |
| 484 * (1) Brings in either black or white pixels from the boundary | |
| 485 * (2) Removes any existing colormap, if necessary, before transforming | |
| 486 * </pre> | |
| 487 */ | |
| 488 PIX * | |
| 489 pixAffine(PIX *pixs, | |
| 490 l_float32 *vc, | |
| 491 l_int32 incolor) | |
| 492 { | |
| 493 l_int32 d; | |
| 494 l_uint32 colorval; | |
| 495 PIX *pixt1, *pixt2, *pixd; | |
| 496 | |
| 497 if (!pixs) | |
| 498 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 499 if (!vc) | |
| 500 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 501 | |
| 502 if (pixGetDepth(pixs) == 1) | |
| 503 return pixAffineSampled(pixs, vc, incolor); | |
| 504 | |
| 505 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ | |
| 506 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 507 d = pixGetDepth(pixt1); | |
| 508 if (d < 8) | |
| 509 pixt2 = pixConvertTo8(pixt1, FALSE); | |
| 510 else | |
| 511 pixt2 = pixClone(pixt1); | |
| 512 d = pixGetDepth(pixt2); | |
| 513 | |
| 514 /* Compute actual color to bring in from edges */ | |
| 515 colorval = 0; | |
| 516 if (incolor == L_BRING_IN_WHITE) { | |
| 517 if (d == 8) | |
| 518 colorval = 255; | |
| 519 else /* d == 32 */ | |
| 520 colorval = 0xffffff00; | |
| 521 } | |
| 522 | |
| 523 if (d == 8) | |
| 524 pixd = pixAffineGray(pixt2, vc, colorval); | |
| 525 else /* d == 32 */ | |
| 526 pixd = pixAffineColor(pixt2, vc, colorval); | |
| 527 pixDestroy(&pixt1); | |
| 528 pixDestroy(&pixt2); | |
| 529 return pixd; | |
| 530 } | |
| 531 | |
| 532 | |
| 533 /*! | |
| 534 * \brief pixAffinePtaColor() | |
| 535 * | |
| 536 * \param[in] pixs 32 bpp | |
| 537 * \param[in] ptad 3 pts of final coordinate space | |
| 538 * \param[in] ptas 3 pts of initial coordinate space | |
| 539 * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE | |
| 540 * \return pixd, or NULL on error | |
| 541 */ | |
| 542 PIX * | |
| 543 pixAffinePtaColor(PIX *pixs, | |
| 544 PTA *ptad, | |
| 545 PTA *ptas, | |
| 546 l_uint32 colorval) | |
| 547 { | |
| 548 l_float32 *vc; | |
| 549 PIX *pixd; | |
| 550 | |
| 551 if (!pixs) | |
| 552 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 553 if (!ptas) | |
| 554 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 555 if (!ptad) | |
| 556 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 557 if (pixGetDepth(pixs) != 32) | |
| 558 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL); | |
| 559 if (ptaGetCount(ptas) != 3) | |
| 560 return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL); | |
| 561 if (ptaGetCount(ptad) != 3) | |
| 562 return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL); | |
| 563 | |
| 564 /* Get backwards transform from dest to src, and apply it */ | |
| 565 getAffineXformCoeffs(ptad, ptas, &vc); | |
| 566 pixd = pixAffineColor(pixs, vc, colorval); | |
| 567 LEPT_FREE(vc); | |
| 568 | |
| 569 return pixd; | |
| 570 } | |
| 571 | |
| 572 | |
| 573 /*! | |
| 574 * \brief pixAffineColor() | |
| 575 * | |
| 576 * \param[in] pixs 32 bpp | |
| 577 * \param[in] vc vector of 6 coefficients for affine transformation | |
| 578 * \param[in] colorval e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE | |
| 579 * \return pixd, or NULL on error | |
| 580 */ | |
| 581 PIX * | |
| 582 pixAffineColor(PIX *pixs, | |
| 583 l_float32 *vc, | |
| 584 l_uint32 colorval) | |
| 585 { | |
| 586 l_int32 i, j, w, h, d, wpls, wpld; | |
| 587 l_uint32 val; | |
| 588 l_uint32 *datas, *datad, *lined; | |
| 589 l_float32 x, y; | |
| 590 PIX *pix1, *pix2, *pixd; | |
| 591 | |
| 592 if (!pixs) | |
| 593 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 594 pixGetDimensions(pixs, &w, &h, &d); | |
| 595 if (d != 32) | |
| 596 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL); | |
| 597 if (!vc) | |
| 598 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 599 | |
| 600 datas = pixGetData(pixs); | |
| 601 wpls = pixGetWpl(pixs); | |
| 602 pixd = pixCreateTemplate(pixs); | |
| 603 pixSetAllArbitrary(pixd, colorval); | |
| 604 datad = pixGetData(pixd); | |
| 605 wpld = pixGetWpl(pixd); | |
| 606 | |
| 607 /* Iterate over destination pixels */ | |
| 608 for (i = 0; i < h; i++) { | |
| 609 lined = datad + i * wpld; | |
| 610 for (j = 0; j < w; j++) { | |
| 611 /* Compute float src pixel location corresponding to (i,j) */ | |
| 612 affineXformPt(vc, j, i, &x, &y); | |
| 613 linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, | |
| 614 &val); | |
| 615 *(lined + j) = val; | |
| 616 } | |
| 617 } | |
| 618 | |
| 619 /* If rgba, transform the pixs alpha channel and insert in pixd */ | |
| 620 if (pixGetSpp(pixs) == 4) { | |
| 621 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); | |
| 622 pix2 = pixAffineGray(pix1, vc, 255); /* bring in opaque */ | |
| 623 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); | |
| 624 pixDestroy(&pix1); | |
| 625 pixDestroy(&pix2); | |
| 626 } | |
| 627 | |
| 628 return pixd; | |
| 629 } | |
| 630 | |
| 631 | |
| 632 /*! | |
| 633 * \brief pixAffinePtaGray() | |
| 634 * | |
| 635 * \param[in] pixs 8 bpp | |
| 636 * \param[in] ptad 3 pts of final coordinate space | |
| 637 * \param[in] ptas 3 pts of initial coordinate space | |
| 638 * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE | |
| 639 * \return pixd, or NULL on error | |
| 640 */ | |
| 641 PIX * | |
| 642 pixAffinePtaGray(PIX *pixs, | |
| 643 PTA *ptad, | |
| 644 PTA *ptas, | |
| 645 l_uint8 grayval) | |
| 646 { | |
| 647 l_float32 *vc; | |
| 648 PIX *pixd; | |
| 649 | |
| 650 if (!pixs) | |
| 651 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 652 if (!ptas) | |
| 653 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 654 if (!ptad) | |
| 655 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 656 if (pixGetDepth(pixs) != 8) | |
| 657 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); | |
| 658 if (ptaGetCount(ptas) != 3) | |
| 659 return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL); | |
| 660 if (ptaGetCount(ptad) != 3) | |
| 661 return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL); | |
| 662 | |
| 663 /* Get backwards transform from dest to src, and apply it */ | |
| 664 getAffineXformCoeffs(ptad, ptas, &vc); | |
| 665 pixd = pixAffineGray(pixs, vc, grayval); | |
| 666 LEPT_FREE(vc); | |
| 667 | |
| 668 return pixd; | |
| 669 } | |
| 670 | |
| 671 | |
| 672 | |
| 673 /*! | |
| 674 * \brief pixAffineGray() | |
| 675 * | |
| 676 * \param[in] pixs 8 bpp | |
| 677 * \param[in] vc vector of 6 coefficients for affine transformation | |
| 678 * \param[in] grayval e.g.: 0 to bring in BLACK, 255 for WHITE | |
| 679 * \return pixd, or NULL on error | |
| 680 */ | |
| 681 PIX * | |
| 682 pixAffineGray(PIX *pixs, | |
| 683 l_float32 *vc, | |
| 684 l_uint8 grayval) | |
| 685 { | |
| 686 l_int32 i, j, w, h, wpls, wpld, val; | |
| 687 l_uint32 *datas, *datad, *lined; | |
| 688 l_float32 x, y; | |
| 689 PIX *pixd; | |
| 690 | |
| 691 if (!pixs) | |
| 692 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 693 pixGetDimensions(pixs, &w, &h, NULL); | |
| 694 if (pixGetDepth(pixs) != 8) | |
| 695 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL); | |
| 696 if (!vc) | |
| 697 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL); | |
| 698 | |
| 699 datas = pixGetData(pixs); | |
| 700 wpls = pixGetWpl(pixs); | |
| 701 pixd = pixCreateTemplate(pixs); | |
| 702 pixSetAllArbitrary(pixd, grayval); | |
| 703 datad = pixGetData(pixd); | |
| 704 wpld = pixGetWpl(pixd); | |
| 705 | |
| 706 /* Iterate over destination pixels */ | |
| 707 for (i = 0; i < h; i++) { | |
| 708 lined = datad + i * wpld; | |
| 709 for (j = 0; j < w; j++) { | |
| 710 /* Compute float src pixel location corresponding to (i,j) */ | |
| 711 affineXformPt(vc, j, i, &x, &y); | |
| 712 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); | |
| 713 SET_DATA_BYTE(lined, j, val); | |
| 714 } | |
| 715 } | |
| 716 | |
| 717 return pixd; | |
| 718 } | |
| 719 | |
| 720 | |
| 721 /*---------------------------------------------------------------------------* | |
| 722 * Affine transform including alpha (blend) component * | |
| 723 *---------------------------------------------------------------------------*/ | |
| 724 /*! | |
| 725 * \brief pixAffinePtaWithAlpha() | |
| 726 * | |
| 727 * \param[in] pixs 32 bpp rgb | |
| 728 * \param[in] ptad 3 pts of final coordinate space | |
| 729 * \param[in] ptas 3 pts of initial coordinate space | |
| 730 * \param[in] pixg [optional] 8 bpp, can be null | |
| 731 * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent | |
| 732 * and 1.0 fully opaque | |
| 733 * \param[in] border of pixels added to capture transformed source pixels | |
| 734 * \return pixd, or NULL on error | |
| 735 * | |
| 736 * <pre> | |
| 737 * Notes: | |
| 738 * (1) The alpha channel is transformed separately from pixs, | |
| 739 * and aligns with it, being fully transparent outside the | |
| 740 * boundary of the transformed pixs. For pixels that are fully | |
| 741 * transparent, a blending function like pixBlendWithGrayMask() | |
| 742 * will give zero weight to corresponding pixels in pixs. | |
| 743 * (2) If pixg is NULL, it is generated as an alpha layer that is | |
| 744 * partially opaque, using %fract. Otherwise, it is cropped | |
| 745 * to pixs if required and %fract is ignored. The alpha channel | |
| 746 * in pixs is never used. | |
| 747 * (3) Colormaps are removed. | |
| 748 * (4) When pixs is transformed, it doesn't matter what color is brought | |
| 749 * in because the alpha channel will be transparent (0) there. | |
| 750 * (5) To avoid losing source pixels in the destination, it may be | |
| 751 * necessary to add a border to the source pix before doing | |
| 752 * the affine transformation. This can be any non-negative number. | |
| 753 * (6) The input %ptad and %ptas are in a coordinate space before | |
| 754 * the border is added. Internally, we compensate for this | |
| 755 * before doing the affine transform on the image after the border | |
| 756 * is added. | |
| 757 * (7) The default setting for the border values in the alpha channel | |
| 758 * is 0 (transparent) for the outermost ring of pixels and | |
| 759 * (0.5 * fract * 255) for the second ring. When blended over | |
| 760 * a second image, this | |
| 761 * (a) shrinks the visible image to make a clean overlap edge | |
| 762 * with an image below, and | |
| 763 * (b) softens the edges by weakening the aliasing there. | |
| 764 * Use l_setAlphaMaskBorder() to change these values. | |
| 765 * </pre> | |
| 766 */ | |
| 767 PIX * | |
| 768 pixAffinePtaWithAlpha(PIX *pixs, | |
| 769 PTA *ptad, | |
| 770 PTA *ptas, | |
| 771 PIX *pixg, | |
| 772 l_float32 fract, | |
| 773 l_int32 border) | |
| 774 { | |
| 775 l_int32 ws, hs, d; | |
| 776 PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; | |
| 777 PTA *ptad2, *ptas2; | |
| 778 | |
| 779 if (!pixs) | |
| 780 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 781 pixGetDimensions(pixs, &ws, &hs, &d); | |
| 782 if (d != 32 && pixGetColormap(pixs) == NULL) | |
| 783 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL); | |
| 784 if (pixg && pixGetDepth(pixg) != 8) { | |
| 785 L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n", | |
| 786 __func__); | |
| 787 pixg = NULL; | |
| 788 } | |
| 789 if (!pixg && (fract < 0.0 || fract > 1.0)) { | |
| 790 L_WARNING("invalid fract; using 1.0 (fully transparent)\n", __func__); | |
| 791 fract = 1.0; | |
| 792 } | |
| 793 if (!pixg && fract == 0.0) | |
| 794 L_WARNING("fully opaque alpha; image will not be blended\n", __func__); | |
| 795 if (!ptad) | |
| 796 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 797 if (!ptas) | |
| 798 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 799 | |
| 800 /* Add border; the color doesn't matter */ | |
| 801 pixb1 = pixAddBorder(pixs, border, 0); | |
| 802 | |
| 803 /* Transform the ptr arrays to work on the bordered image */ | |
| 804 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); | |
| 805 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); | |
| 806 | |
| 807 /* Do separate affine transform of rgb channels of pixs and of pixg */ | |
| 808 pixd = pixAffinePtaColor(pixb1, ptad2, ptas2, 0); | |
| 809 if (!pixg) { | |
| 810 pixg2 = pixCreate(ws, hs, 8); | |
| 811 if (fract == 1.0) | |
| 812 pixSetAll(pixg2); | |
| 813 else | |
| 814 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); | |
| 815 } else { | |
| 816 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); | |
| 817 } | |
| 818 if (ws > 10 && hs > 10) { /* see note 7 */ | |
| 819 pixSetBorderRingVal(pixg2, 1, | |
| 820 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); | |
| 821 pixSetBorderRingVal(pixg2, 2, | |
| 822 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); | |
| 823 | |
| 824 } | |
| 825 pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ | |
| 826 pixga = pixAffinePtaGray(pixb2, ptad2, ptas2, 0); | |
| 827 pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); | |
| 828 pixSetSpp(pixd, 4); | |
| 829 | |
| 830 pixDestroy(&pixg2); | |
| 831 pixDestroy(&pixb1); | |
| 832 pixDestroy(&pixb2); | |
| 833 pixDestroy(&pixga); | |
| 834 ptaDestroy(&ptad2); | |
| 835 ptaDestroy(&ptas2); | |
| 836 return pixd; | |
| 837 } | |
| 838 | |
| 839 | |
| 840 /*-------------------------------------------------------------* | |
| 841 * Affine coordinate transformation * | |
| 842 *-------------------------------------------------------------*/ | |
| 843 /*! | |
| 844 * \brief getAffineXformCoeffs() | |
| 845 * | |
| 846 * \param[in] ptas source 3 points; unprimed | |
| 847 * \param[in] ptad transformed 3 points; primed | |
| 848 * \param[out] pvc vector of coefficients of transform | |
| 849 * \return 0 if OK; 1 on error | |
| 850 * | |
| 851 * <pre> | |
| 852 * We have a set of six equations, describing the affine | |
| 853 * transformation that takes 3 points ptas into 3 other | |
| 854 * points ptad. These equations are: | |
| 855 * | |
| 856 * x1' = c[0]*x1 + c[1]*y1 + c[2] | |
| 857 * y1' = c[3]*x1 + c[4]*y1 + c[5] | |
| 858 * x2' = c[0]*x2 + c[1]*y2 + c[2] | |
| 859 * y2' = c[3]*x2 + c[4]*y2 + c[5] | |
| 860 * x3' = c[0]*x3 + c[1]*y3 + c[2] | |
| 861 * y3' = c[3]*x3 + c[4]*y3 + c[5] | |
| 862 * | |
| 863 * This can be represented as | |
| 864 * | |
| 865 * AC = B | |
| 866 * | |
| 867 * where B and C are column vectors | |
| 868 * | |
| 869 * B = [ x1' y1' x2' y2' x3' y3' ] | |
| 870 * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] ] | |
| 871 * | |
| 872 * and A is the 6x6 matrix | |
| 873 * | |
| 874 * x1 y1 1 0 0 0 | |
| 875 * 0 0 0 x1 y1 1 | |
| 876 * x2 y2 1 0 0 0 | |
| 877 * 0 0 0 x2 y2 1 | |
| 878 * x3 y3 1 0 0 0 | |
| 879 * 0 0 0 x3 y3 1 | |
| 880 * | |
| 881 * These six equations are solved here for the coefficients C. | |
| 882 * | |
| 883 * These six coefficients can then be used to find the dest | |
| 884 * point x',y') corresponding to any src point (x,y, according | |
| 885 * to the equations | |
| 886 * | |
| 887 * x' = c[0]x + c[1]y + c[2] | |
| 888 * y' = c[3]x + c[4]y + c[5] | |
| 889 * | |
| 890 * that are implemented in affineXformPt. | |
| 891 * | |
| 892 * !!!!!!!!!!!!!!!!!! Very important !!!!!!!!!!!!!!!!!!!!!! | |
| 893 * | |
| 894 * When the affine transform is composed from a set of simple | |
| 895 * operations such as translation, scaling and rotation, | |
| 896 * it is built in a form to convert from the un-transformed src | |
| 897 * point to the transformed dest point. However, when an | |
| 898 * affine transform is used on images, it is used in an inverted | |
| 899 * way: it converts from the transformed dest point to the | |
| 900 * un-transformed src point. So, for example, if you transform | |
| 901 * a boxa using transform A, to transform an image in the same | |
| 902 * way you must use the inverse of A. | |
| 903 * | |
| 904 * For example, if you transform a boxa with a 3x3 affine matrix | |
| 905 * 'mat', the analogous image transformation must use 'matinv': | |
| 906 * \code | |
| 907 * boxad = boxaAffineTransform(boxas, mat); | |
| 908 * affineInvertXform(mat, &matinv); | |
| 909 * pixd = pixAffine(pixs, matinv, L_BRING_IN_WHITE); | |
| 910 * \endcode | |
| 911 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
| 912 * </pre> | |
| 913 */ | |
| 914 l_ok | |
| 915 getAffineXformCoeffs(PTA *ptas, | |
| 916 PTA *ptad, | |
| 917 l_float32 **pvc) | |
| 918 { | |
| 919 l_int32 i; | |
| 920 l_float32 x1, y1, x2, y2, x3, y3; | |
| 921 l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ | |
| 922 l_float32 *a[6]; /* 6x6 matrix A */ | |
| 923 | |
| 924 if (!ptas) | |
| 925 return ERROR_INT("ptas not defined", __func__, 1); | |
| 926 if (!ptad) | |
| 927 return ERROR_INT("ptad not defined", __func__, 1); | |
| 928 if (!pvc) | |
| 929 return ERROR_INT("&vc not defined", __func__, 1); | |
| 930 | |
| 931 b = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); | |
| 932 *pvc = b; | |
| 933 | |
| 934 ptaGetPt(ptas, 0, &x1, &y1); | |
| 935 ptaGetPt(ptas, 1, &x2, &y2); | |
| 936 ptaGetPt(ptas, 2, &x3, &y3); | |
| 937 ptaGetPt(ptad, 0, &b[0], &b[1]); | |
| 938 ptaGetPt(ptad, 1, &b[2], &b[3]); | |
| 939 ptaGetPt(ptad, 2, &b[4], &b[5]); | |
| 940 | |
| 941 for (i = 0; i < 6; i++) | |
| 942 a[i] = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); | |
| 943 a[0][0] = x1; | |
| 944 a[0][1] = y1; | |
| 945 a[0][2] = 1.; | |
| 946 a[1][3] = x1; | |
| 947 a[1][4] = y1; | |
| 948 a[1][5] = 1.; | |
| 949 a[2][0] = x2; | |
| 950 a[2][1] = y2; | |
| 951 a[2][2] = 1.; | |
| 952 a[3][3] = x2; | |
| 953 a[3][4] = y2; | |
| 954 a[3][5] = 1.; | |
| 955 a[4][0] = x3; | |
| 956 a[4][1] = y3; | |
| 957 a[4][2] = 1.; | |
| 958 a[5][3] = x3; | |
| 959 a[5][4] = y3; | |
| 960 a[5][5] = 1.; | |
| 961 | |
| 962 gaussjordan(a, b, 6); | |
| 963 | |
| 964 for (i = 0; i < 6; i++) | |
| 965 LEPT_FREE(a[i]); | |
| 966 | |
| 967 return 0; | |
| 968 } | |
| 969 | |
| 970 | |
| 971 /*! | |
| 972 * \brief affineInvertXform() | |
| 973 * | |
| 974 * \param[in] vc vector of 6 coefficients | |
| 975 * \param[out] pvci inverted transform | |
| 976 * \return 0 if OK; 1 on error | |
| 977 * | |
| 978 * <pre> | |
| 979 * Notes: | |
| 980 * (1) The 6 affine transform coefficients are the first | |
| 981 * two rows of a 3x3 matrix where the last row has | |
| 982 * only a 1 in the third column. We invert this | |
| 983 * using gaussjordan(), and select the first 2 rows | |
| 984 * as the coefficients of the inverse affine transform. | |
| 985 * (2) Alternatively, we can find the inverse transform | |
| 986 * coefficients by inverting the 2x2 submatrix, | |
| 987 * and treating the top 2 coefficients in the 3rd column as | |
| 988 * a RHS vector for that 2x2 submatrix. Then the | |
| 989 * 6 inverted transform coefficients are composed of | |
| 990 * the inverted 2x2 submatrix and the negative of the | |
| 991 * transformed RHS vector. Why is this so? We have | |
| 992 * Y = AX + R (2 equations in 6 unknowns) | |
| 993 * Then | |
| 994 * X = A'Y - A'R | |
| 995 * Gauss-jordan solves | |
| 996 * AF = R | |
| 997 * and puts the solution for F, which is A'R, | |
| 998 * into the input R vector. | |
| 999 * | |
| 1000 * </pre> | |
| 1001 */ | |
| 1002 l_ok | |
| 1003 affineInvertXform(l_float32 *vc, | |
| 1004 l_float32 **pvci) | |
| 1005 { | |
| 1006 l_int32 i; | |
| 1007 l_float32 *vci; | |
| 1008 l_float32 *a[3]; | |
| 1009 l_float32 b[3] = {1.0, 1.0, 1.0}; /* anything; results ignored */ | |
| 1010 | |
| 1011 if (!pvci) | |
| 1012 return ERROR_INT("&vci not defined", __func__, 1); | |
| 1013 *pvci = NULL; | |
| 1014 if (!vc) | |
| 1015 return ERROR_INT("vc not defined", __func__, 1); | |
| 1016 | |
| 1017 #if 1 | |
| 1018 for (i = 0; i < 3; i++) | |
| 1019 a[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); | |
| 1020 a[0][0] = vc[0]; | |
| 1021 a[0][1] = vc[1]; | |
| 1022 a[0][2] = vc[2]; | |
| 1023 a[1][0] = vc[3]; | |
| 1024 a[1][1] = vc[4]; | |
| 1025 a[1][2] = vc[5]; | |
| 1026 a[2][2] = 1.0; | |
| 1027 gaussjordan(a, b, 3); /* this inverts matrix a */ | |
| 1028 vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); | |
| 1029 *pvci = vci; | |
| 1030 vci[0] = a[0][0]; | |
| 1031 vci[1] = a[0][1]; | |
| 1032 vci[2] = a[0][2]; | |
| 1033 vci[3] = a[1][0]; | |
| 1034 vci[4] = a[1][1]; | |
| 1035 vci[5] = a[1][2]; | |
| 1036 for (i = 0; i < 3; i++) | |
| 1037 LEPT_FREE(a[i]); | |
| 1038 | |
| 1039 #else | |
| 1040 | |
| 1041 /* Alternative version, inverting a 2x2 matrix */ | |
| 1042 { l_float32 *a2[2]; | |
| 1043 for (i = 0; i < 2; i++) | |
| 1044 a2[i] = (l_float32 *)LEPT_CALLOC(2, sizeof(l_float32)); | |
| 1045 a2[0][0] = vc[0]; | |
| 1046 a2[0][1] = vc[1]; | |
| 1047 a2[1][0] = vc[3]; | |
| 1048 a2[1][1] = vc[4]; | |
| 1049 b[0] = vc[2]; | |
| 1050 b[1] = vc[5]; | |
| 1051 gaussjordan(a2, b, 2); /* this inverts matrix a2 */ | |
| 1052 vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32)); | |
| 1053 *pvci = vci; | |
| 1054 vci[0] = a2[0][0]; | |
| 1055 vci[1] = a2[0][1]; | |
| 1056 vci[2] = -b[0]; /* note sign */ | |
| 1057 vci[3] = a2[1][0]; | |
| 1058 vci[4] = a2[1][1]; | |
| 1059 vci[5] = -b[1]; /* note sign */ | |
| 1060 for (i = 0; i < 2; i++) | |
| 1061 LEPT_FREE(a2[i]); | |
| 1062 } | |
| 1063 #endif | |
| 1064 | |
| 1065 return 0; | |
| 1066 } | |
| 1067 | |
| 1068 | |
| 1069 /*! | |
| 1070 * \brief affineXformSampledPt() | |
| 1071 * | |
| 1072 * \param[in] vc vector of 6 coefficients | |
| 1073 * \param[in] x, y initial point | |
| 1074 * \param[out] pxp, pyp transformed point | |
| 1075 * \return 0 if OK; 1 on error | |
| 1076 * | |
| 1077 * <pre> | |
| 1078 * Notes: | |
| 1079 * (1) This finds the nearest pixel coordinates of the transformed point. | |
| 1080 * (2) It does not check ptrs for returned data! | |
| 1081 * </pre> | |
| 1082 */ | |
| 1083 l_ok | |
| 1084 affineXformSampledPt(l_float32 *vc, | |
| 1085 l_int32 x, | |
| 1086 l_int32 y, | |
| 1087 l_int32 *pxp, | |
| 1088 l_int32 *pyp) | |
| 1089 { | |
| 1090 if (!vc) | |
| 1091 return ERROR_INT("vc not defined", __func__, 1); | |
| 1092 | |
| 1093 *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] + 0.5); | |
| 1094 *pyp = (l_int32)(vc[3] * x + vc[4] * y + vc[5] + 0.5); | |
| 1095 return 0; | |
| 1096 } | |
| 1097 | |
| 1098 | |
| 1099 /*! | |
| 1100 * \brief affineXformPt() | |
| 1101 * | |
| 1102 * \param[in] vc vector of 6 coefficients | |
| 1103 * \param[in] x, y initial point | |
| 1104 * \param[out] pxp, pyp transformed point | |
| 1105 * \return 0 if OK; 1 on error | |
| 1106 * | |
| 1107 * <pre> | |
| 1108 * Notes: | |
| 1109 * (1) This computes the floating point location of the transformed point. | |
| 1110 * (2) It does not check ptrs for returned data! | |
| 1111 * </pre> | |
| 1112 */ | |
| 1113 l_ok | |
| 1114 affineXformPt(l_float32 *vc, | |
| 1115 l_int32 x, | |
| 1116 l_int32 y, | |
| 1117 l_float32 *pxp, | |
| 1118 l_float32 *pyp) | |
| 1119 { | |
| 1120 if (!vc) | |
| 1121 return ERROR_INT("vc not defined", __func__, 1); | |
| 1122 | |
| 1123 *pxp = vc[0] * x + vc[1] * y + vc[2]; | |
| 1124 *pyp = vc[3] * x + vc[4] * y + vc[5]; | |
| 1125 return 0; | |
| 1126 } | |
| 1127 | |
| 1128 | |
| 1129 /*-------------------------------------------------------------* | |
| 1130 * Interpolation helper functions * | |
| 1131 *-------------------------------------------------------------*/ | |
| 1132 /*! | |
| 1133 * \brief linearInterpolatePixelColor() | |
| 1134 * | |
| 1135 * \param[in] datas ptr to beginning of image data | |
| 1136 * \param[in] wpls 32-bit word/line for this data array | |
| 1137 * \param[in] w, h of image | |
| 1138 * \param[in] x, y floating pt location for evaluation | |
| 1139 * \param[in] colorval color brought in from the outside when the | |
| 1140 * input x,y location is outside the image; | |
| 1141 * in 0xrrggbb00 format) | |
| 1142 * \param[out] pval interpolated color value | |
| 1143 * \return 0 if OK, 1 on error | |
| 1144 * | |
| 1145 * <pre> | |
| 1146 * Notes: | |
| 1147 * (1) This is a standard linear interpolation function. It is | |
| 1148 * equivalent to area weighting on each component, and | |
| 1149 * avoids "jaggies" when rendering sharp edges. | |
| 1150 * </pre> | |
| 1151 */ | |
| 1152 l_ok | |
| 1153 linearInterpolatePixelColor(l_uint32 *datas, | |
| 1154 l_int32 wpls, | |
| 1155 l_int32 w, | |
| 1156 l_int32 h, | |
| 1157 l_float32 x, | |
| 1158 l_float32 y, | |
| 1159 l_uint32 colorval, | |
| 1160 l_uint32 *pval) | |
| 1161 { | |
| 1162 l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf; | |
| 1163 l_int32 rval, gval, bval; | |
| 1164 l_uint32 word00, word01, word10, word11; | |
| 1165 l_uint32 *lines; | |
| 1166 | |
| 1167 if (!pval) | |
| 1168 return ERROR_INT("&val not defined", __func__, 1); | |
| 1169 *pval = colorval; | |
| 1170 if (!datas) | |
| 1171 return ERROR_INT("datas not defined", __func__, 1); | |
| 1172 | |
| 1173 /* Skip if x or y are invalid. (x,y) must be in the source image. | |
| 1174 * Failure to detect an invalid point will cause a mem address fault. | |
| 1175 * Occasionally, x or y will be a nan, and relational checks always | |
| 1176 * fail for nans. Therefore we check if the point is inside the pix */ | |
| 1177 valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); | |
| 1178 if (!valid) return 0; | |
| 1179 | |
| 1180 xpm = (l_int32)(16.0 * x); | |
| 1181 ypm = (l_int32)(16.0 * y); | |
| 1182 xp = xpm >> 4; | |
| 1183 xp2 = xp + 1 < w ? xp + 1 : xp; | |
| 1184 yp = ypm >> 4; | |
| 1185 if (yp + 1 >= h) wpls = 0; | |
| 1186 xf = xpm & 0x0f; | |
| 1187 yf = ypm & 0x0f; | |
| 1188 | |
| 1189 #if DEBUG | |
| 1190 if (xf < 0 || yf < 0) | |
| 1191 lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); | |
| 1192 #endif /* DEBUG */ | |
| 1193 | |
| 1194 /* Do area weighting (eqiv. to linear interpolation) */ | |
| 1195 lines = datas + yp * wpls; | |
| 1196 word00 = *(lines + xp); | |
| 1197 word10 = *(lines + xp2); | |
| 1198 word01 = *(lines + wpls + xp); | |
| 1199 word11 = *(lines + wpls + xp2); | |
| 1200 rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + | |
| 1201 xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + | |
| 1202 (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + | |
| 1203 xf * yf * ((word11 >> L_RED_SHIFT) & 0xff)) / 256; | |
| 1204 gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + | |
| 1205 xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + | |
| 1206 (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + | |
| 1207 xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff)) / 256; | |
| 1208 bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + | |
| 1209 xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + | |
| 1210 (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + | |
| 1211 xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff)) / 256; | |
| 1212 composeRGBPixel(rval, gval, bval, pval); | |
| 1213 return 0; | |
| 1214 } | |
| 1215 | |
| 1216 | |
| 1217 /*! | |
| 1218 * \brief linearInterpolatePixelGray() | |
| 1219 * | |
| 1220 * \param[in] datas ptr to beginning of image data | |
| 1221 * \param[in] wpls 32-bit word/line for this data array | |
| 1222 * \param[in] w, h of image | |
| 1223 * \param[in] x, y floating pt location for evaluation | |
| 1224 * \param[in] grayval color brought in from the outside when the | |
| 1225 * input x,y location is outside the image | |
| 1226 * \param[out] pval interpolated gray value | |
| 1227 * \return 0 if OK, 1 on error | |
| 1228 * | |
| 1229 * <pre> | |
| 1230 * Notes: | |
| 1231 * (1) This is a standard linear interpolation function. It is | |
| 1232 * equivalent to area weighting on each component, and | |
| 1233 * avoids "jaggies" when rendering sharp edges. | |
| 1234 * </pre> | |
| 1235 */ | |
| 1236 l_ok | |
| 1237 linearInterpolatePixelGray(l_uint32 *datas, | |
| 1238 l_int32 wpls, | |
| 1239 l_int32 w, | |
| 1240 l_int32 h, | |
| 1241 l_float32 x, | |
| 1242 l_float32 y, | |
| 1243 l_int32 grayval, | |
| 1244 l_int32 *pval) | |
| 1245 { | |
| 1246 l_int32 valid, xpm, ypm, xp, xp2, yp, xf, yf, v00, v10, v01, v11; | |
| 1247 l_uint32 *lines; | |
| 1248 | |
| 1249 if (!pval) | |
| 1250 return ERROR_INT("&val not defined", __func__, 1); | |
| 1251 *pval = grayval; | |
| 1252 if (!datas) | |
| 1253 return ERROR_INT("datas not defined", __func__, 1); | |
| 1254 | |
| 1255 /* Skip if x or y is invalid. (x,y) must be in the source image. | |
| 1256 * Failure to detect an invalid point will cause a mem address fault. | |
| 1257 * Occasionally, x or y will be a nan, and relational checks always | |
| 1258 * fail for nans. Therefore we check if the point is inside the pix */ | |
| 1259 valid = (x >= 0.0 && y >= 0.0 && x < w && y < h); | |
| 1260 if (!valid) return 0; | |
| 1261 | |
| 1262 xpm = (l_int32)(16.0 * x); | |
| 1263 ypm = (l_int32)(16.0 * y); | |
| 1264 xp = xpm >> 4; | |
| 1265 xp2 = xp + 1 < w ? xp + 1 : xp; | |
| 1266 yp = ypm >> 4; | |
| 1267 if (yp + 1 >= h) wpls = 0; | |
| 1268 xf = xpm & 0x0f; | |
| 1269 yf = ypm & 0x0f; | |
| 1270 | |
| 1271 #if DEBUG | |
| 1272 if (xf < 0 || yf < 0) | |
| 1273 lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); | |
| 1274 #endif /* DEBUG */ | |
| 1275 | |
| 1276 /* Interpolate by area weighting. */ | |
| 1277 lines = datas + yp * wpls; | |
| 1278 v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); | |
| 1279 v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp2); | |
| 1280 v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); | |
| 1281 v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp2); | |
| 1282 *pval = (v00 + v01 + v10 + v11) / 256; | |
| 1283 return 0; | |
| 1284 } | |
| 1285 | |
| 1286 | |
| 1287 | |
| 1288 /*-------------------------------------------------------------* | |
| 1289 * Gauss-jordan linear equation solver * | |
| 1290 *-------------------------------------------------------------*/ | |
| 1291 #define SWAP(a,b) {temp = (a); (a) = (b); (b) = temp;} | |
| 1292 | |
| 1293 /*! | |
| 1294 * \brief gaussjordan() | |
| 1295 * | |
| 1296 * \param[in] a n x n matrix | |
| 1297 * \param[in] b n x 1 right-hand side column vector | |
| 1298 * \param[in] n dimension | |
| 1299 * \return 0 if ok, 1 on error | |
| 1300 * | |
| 1301 * <pre> | |
| 1302 * Notes: | |
| 1303 * (1) There are two side-effects: | |
| 1304 * * The matrix a is transformed to its inverse A | |
| 1305 * * The rhs vector b is transformed to the solution x | |
| 1306 * of the linear equation ax = b | |
| 1307 * (2) The inverse A can then be used to solve the same equation with | |
| 1308 * different rhs vectors c by multiplication: x = Ac | |
| 1309 * (3) Adapted from "Numerical Recipes in C, Second Edition", 1992, | |
| 1310 * pp. 36-41 (gauss-jordan elimination) | |
| 1311 * </pre> | |
| 1312 */ | |
| 1313 l_int32 | |
| 1314 gaussjordan(l_float32 **a, | |
| 1315 l_float32 *b, | |
| 1316 l_int32 n) | |
| 1317 { | |
| 1318 l_int32 i, icol, irow, j, k, col, row, success; | |
| 1319 l_int32 *indexc, *indexr, *ipiv; | |
| 1320 l_float32 maxval, val, pivinv, temp; | |
| 1321 | |
| 1322 if (!a) | |
| 1323 return ERROR_INT("a not defined", __func__, 1); | |
| 1324 if (!b) | |
| 1325 return ERROR_INT("b not defined", __func__, 1); | |
| 1326 | |
| 1327 success = TRUE; | |
| 1328 indexc = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); | |
| 1329 indexr = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); | |
| 1330 ipiv = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32)); | |
| 1331 if (!indexc || !indexr || !ipiv) { | |
| 1332 L_ERROR("array not made\n", __func__); | |
| 1333 success = FALSE; | |
| 1334 goto cleanup_arrays; | |
| 1335 } | |
| 1336 | |
| 1337 icol = irow = 0; /* silence static checker */ | |
| 1338 for (i = 0; i < n; i++) { | |
| 1339 maxval = 0.0; | |
| 1340 for (j = 0; j < n; j++) { | |
| 1341 if (ipiv[j] != 1) { | |
| 1342 for (k = 0; k < n; k++) { | |
| 1343 if (ipiv[k] == 0) { | |
| 1344 if (fabs(a[j][k]) >= maxval) { | |
| 1345 maxval = fabs(a[j][k]); | |
| 1346 irow = j; | |
| 1347 icol = k; | |
| 1348 } | |
| 1349 } else if (ipiv[k] > 1) { | |
| 1350 L_ERROR("singular matrix\n", __func__); | |
| 1351 success = FALSE; | |
| 1352 goto cleanup_arrays; | |
| 1353 } | |
| 1354 } | |
| 1355 } | |
| 1356 } | |
| 1357 ++(ipiv[icol]); | |
| 1358 | |
| 1359 if (irow != icol) { | |
| 1360 for (col = 0; col < n; col++) | |
| 1361 SWAP(a[irow][col], a[icol][col]); | |
| 1362 SWAP(b[irow], b[icol]); | |
| 1363 } | |
| 1364 | |
| 1365 indexr[i] = irow; | |
| 1366 indexc[i] = icol; | |
| 1367 if (a[icol][icol] == 0.0) { | |
| 1368 L_ERROR("singular matrix\n", __func__); | |
| 1369 success = FALSE; | |
| 1370 goto cleanup_arrays; | |
| 1371 } | |
| 1372 pivinv = 1.0 / a[icol][icol]; | |
| 1373 a[icol][icol] = 1.0; | |
| 1374 for (col = 0; col < n; col++) | |
| 1375 a[icol][col] *= pivinv; | |
| 1376 b[icol] *= pivinv; | |
| 1377 | |
| 1378 for (row = 0; row < n; row++) { | |
| 1379 if (row != icol) { | |
| 1380 val = a[row][icol]; | |
| 1381 a[row][icol] = 0.0; | |
| 1382 for (col = 0; col < n; col++) | |
| 1383 a[row][col] -= a[icol][col] * val; | |
| 1384 b[row] -= b[icol] * val; | |
| 1385 } | |
| 1386 } | |
| 1387 } | |
| 1388 | |
| 1389 for (col = n - 1; col >= 0; col--) { | |
| 1390 if (indexr[col] != indexc[col]) { | |
| 1391 for (k = 0; k < n; k++) | |
| 1392 SWAP(a[k][indexr[col]], a[k][indexc[col]]); | |
| 1393 } | |
| 1394 } | |
| 1395 | |
| 1396 cleanup_arrays: | |
| 1397 LEPT_FREE(indexr); | |
| 1398 LEPT_FREE(indexc); | |
| 1399 LEPT_FREE(ipiv); | |
| 1400 return (success) ? 0 : 1; | |
| 1401 } | |
| 1402 | |
| 1403 | |
| 1404 /*-------------------------------------------------------------* | |
| 1405 * Sequential affine image transformation * | |
| 1406 *-------------------------------------------------------------*/ | |
| 1407 /*! | |
| 1408 * \brief pixAffineSequential() | |
| 1409 * | |
| 1410 * \param[in] pixs | |
| 1411 * \param[in] ptad 3 pts of final coordinate space | |
| 1412 * \param[in] ptas 3 pts of initial coordinate space | |
| 1413 * \param[in] bw pixels of additional border width during computation | |
| 1414 * \param[in] bh pixels of additional border height during computation | |
| 1415 * \return pixd, or NULL on error | |
| 1416 * | |
| 1417 * <pre> | |
| 1418 * Notes: | |
| 1419 * (1) The 3 pts must not be collinear. | |
| 1420 * (2) The 3 pts must be given in this order: | |
| 1421 * ~ origin | |
| 1422 * ~ a location along the x-axis | |
| 1423 * ~ a location along the y-axis. | |
| 1424 * (3) You must guess how much border must be added so that no | |
| 1425 * pixels are lost in the transformations from src to | |
| 1426 * dest coordinate space. (This can be calculated but it | |
| 1427 * is a lot of work!) For coordinate spaces that are nearly | |
| 1428 * at right angles, on a 300 ppi scanned page, the addition | |
| 1429 * of 1000 pixels on each side is usually sufficient. | |
| 1430 * (4) This is here for pedagogical reasons. It is about 3x faster | |
| 1431 * on 1 bpp images than pixAffineSampled(), but the results | |
| 1432 * on text are much inferior. | |
| 1433 * </pre> | |
| 1434 */ | |
| 1435 PIX * | |
| 1436 pixAffineSequential(PIX *pixs, | |
| 1437 PTA *ptad, | |
| 1438 PTA *ptas, | |
| 1439 l_int32 bw, | |
| 1440 l_int32 bh) | |
| 1441 { | |
| 1442 l_int32 x1, y1, x2, y2, x3, y3; /* ptas */ | |
| 1443 l_int32 x1p, y1p, x2p, y2p, x3p, y3p; /* ptad */ | |
| 1444 l_int32 x1sc, y1sc; /* scaled origin */ | |
| 1445 l_float32 x2s, x2sp, scalex, scaley; | |
| 1446 l_float32 th3, th3p, ph2, ph2p; | |
| 1447 #if DEBUG | |
| 1448 l_float32 rad2deg; | |
| 1449 #endif /* DEBUG */ | |
| 1450 PIX *pix1, *pix2, *pixd; | |
| 1451 | |
| 1452 if (!pixs) | |
| 1453 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1454 if (!ptas) | |
| 1455 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 1456 if (!ptad) | |
| 1457 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL); | |
| 1458 | |
| 1459 if (ptaGetCount(ptas) != 3) | |
| 1460 return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL); | |
| 1461 if (ptaGetCount(ptad) != 3) | |
| 1462 return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL); | |
| 1463 ptaGetIPt(ptas, 0, &x1, &y1); | |
| 1464 ptaGetIPt(ptas, 1, &x2, &y2); | |
| 1465 ptaGetIPt(ptas, 2, &x3, &y3); | |
| 1466 ptaGetIPt(ptad, 0, &x1p, &y1p); | |
| 1467 ptaGetIPt(ptad, 1, &x2p, &y2p); | |
| 1468 ptaGetIPt(ptad, 2, &x3p, &y3p); | |
| 1469 | |
| 1470 pix1 = pix2 = pixd = NULL; | |
| 1471 | |
| 1472 if (y1 == y3) | |
| 1473 return (PIX *)ERROR_PTR("y1 == y3!", __func__, NULL); | |
| 1474 if (y1p == y3p) | |
| 1475 return (PIX *)ERROR_PTR("y1p == y3p!", __func__, NULL); | |
| 1476 | |
| 1477 if (bw != 0 || bh != 0) { | |
| 1478 /* resize all points and add border to pixs */ | |
| 1479 x1 = x1 + bw; | |
| 1480 y1 = y1 + bh; | |
| 1481 x2 = x2 + bw; | |
| 1482 y2 = y2 + bh; | |
| 1483 x3 = x3 + bw; | |
| 1484 y3 = y3 + bh; | |
| 1485 x1p = x1p + bw; | |
| 1486 y1p = y1p + bh; | |
| 1487 x2p = x2p + bw; | |
| 1488 y2p = y2p + bh; | |
| 1489 x3p = x3p + bw; | |
| 1490 y3p = y3p + bh; | |
| 1491 | |
| 1492 if ((pix1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL) | |
| 1493 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 1494 } else { | |
| 1495 pix1 = pixCopy(NULL, pixs); | |
| 1496 } | |
| 1497 | |
| 1498 /*-------------------------------------------------------------* | |
| 1499 The horizontal shear is done to move the 3rd point to the | |
| 1500 y axis. This moves the 2nd point either towards or away | |
| 1501 from the y axis, depending on whether it is above or below | |
| 1502 the x axis. That motion must be computed so that we know | |
| 1503 the angle of vertical shear to use to get the 2nd point | |
| 1504 on the x axis. We must also know the x coordinate of the | |
| 1505 2nd point in order to compute how much scaling is required | |
| 1506 to match points on the axis. | |
| 1507 *-------------------------------------------------------------*/ | |
| 1508 | |
| 1509 /* Shear angles required to put src points on x and y axes */ | |
| 1510 th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3)); | |
| 1511 x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3)); | |
| 1512 if (x2s == (l_float32)x1) { | |
| 1513 L_ERROR("x2s == x1!\n", __func__); | |
| 1514 goto cleanup_pix; | |
| 1515 } | |
| 1516 ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1)); | |
| 1517 | |
| 1518 /* Shear angles required to put dest points on x and y axes. | |
| 1519 * Use the negative of these values to instead move the | |
| 1520 * src points from the axes to the actual dest position. | |
| 1521 * These values are also needed to scale the image. */ | |
| 1522 th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p)); | |
| 1523 x2sp = (l_float32)(x2p - | |
| 1524 ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p)); | |
| 1525 if (x2sp == (l_float32)x1p) { | |
| 1526 L_ERROR("x2sp == x1p!\n", __func__); | |
| 1527 goto cleanup_pix; | |
| 1528 } | |
| 1529 ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p)); | |
| 1530 | |
| 1531 /* Shear image to first put src point 3 on the y axis, | |
| 1532 * and then to put src point 2 on the x axis */ | |
| 1533 pixHShearIP(pix1, y1, th3, L_BRING_IN_WHITE); | |
| 1534 pixVShearIP(pix1, x1, ph2, L_BRING_IN_WHITE); | |
| 1535 | |
| 1536 /* Scale image to match dest scale. The dest scale | |
| 1537 * is calculated above from the angles th3p and ph2p | |
| 1538 * that would be required to move the dest points to | |
| 1539 * the x and y axes. */ | |
| 1540 scalex = (l_float32)(x2sp - x1p) / (x2s - x1); | |
| 1541 scaley = (l_float32)(y3p - y1p) / (y3 - y1); | |
| 1542 if ((pix2 = pixScale(pix1, scalex, scaley)) == NULL) { | |
| 1543 L_ERROR("pix2 not made\n", __func__); | |
| 1544 goto cleanup_pix; | |
| 1545 } | |
| 1546 | |
| 1547 #if DEBUG | |
| 1548 rad2deg = 180. / 3.1415926535; | |
| 1549 lept_stderr("th3 = %5.1f deg, ph2 = %5.1f deg\n", | |
| 1550 rad2deg * th3, rad2deg * ph2); | |
| 1551 lept_stderr("th3' = %5.1f deg, ph2' = %5.1f deg\n", | |
| 1552 rad2deg * th3p, rad2deg * ph2p); | |
| 1553 lept_stderr("scalex = %6.3f, scaley = %6.3f\n", scalex, scaley); | |
| 1554 #endif /* DEBUG */ | |
| 1555 | |
| 1556 /*-------------------------------------------------------------* | |
| 1557 Scaling moves the 1st src point, which is the origin. | |
| 1558 It must now be moved again to coincide with the origin | |
| 1559 (1st point) of the dest. After this is done, the 2nd | |
| 1560 and 3rd points must be sheared back to the original | |
| 1561 positions of the 2nd and 3rd dest points. We use the | |
| 1562 negative of the angles that were previously computed | |
| 1563 for shearing those points in the dest image to x and y | |
| 1564 axes, and take the shears in reverse order as well. | |
| 1565 *-------------------------------------------------------------*/ | |
| 1566 /* Shift image to match dest origin. */ | |
| 1567 x1sc = (l_int32)(scalex * x1 + 0.5); /* x comp of origin after scaling */ | |
| 1568 y1sc = (l_int32)(scaley * y1 + 0.5); /* y comp of origin after scaling */ | |
| 1569 pixRasteropIP(pix2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE); | |
| 1570 | |
| 1571 /* Shear image to take points 2 and 3 off the axis and | |
| 1572 * put them in the original dest position */ | |
| 1573 pixVShearIP(pix2, x1p, -ph2p, L_BRING_IN_WHITE); | |
| 1574 pixHShearIP(pix2, y1p, -th3p, L_BRING_IN_WHITE); | |
| 1575 | |
| 1576 if (bw != 0 || bh != 0) { | |
| 1577 if ((pixd = pixRemoveBorderGeneral(pix2, bw, bw, bh, bh)) == NULL) | |
| 1578 L_ERROR("pixd not made\n", __func__); | |
| 1579 } else { | |
| 1580 pixd = pixClone(pix2); | |
| 1581 } | |
| 1582 | |
| 1583 cleanup_pix: | |
| 1584 pixDestroy(&pix1); | |
| 1585 pixDestroy(&pix2); | |
| 1586 return pixd; | |
| 1587 } |
