Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 /* 00018 * affine.c 00019 * 00020 * Affine (3 pt) image transformation using a sampled 00021 * (to nearest integer) transform on each dest point 00022 * PIX *pixAffineSampledPta() 00023 * PIX *pixAffineSampled() 00024 * 00025 * Affine (3 pt) image transformation using interpolation 00026 * (or area mapping) for anti-aliasing images that are 00027 * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB 00028 * PIX *pixAffinePta() 00029 * PIX *pixAffine() 00030 * PIX *pixAffinePtaColor() 00031 * PIX *pixAffineColor() 00032 * PIX *pixAffinePtaGray() 00033 * PIX *pixAffineGray() 00034 * 00035 * Affine transform including alpha (blend) component and gamma transform 00036 * PIX *pixAffinePtaWithAlpha() 00037 * PIX *pixAffinePtaGammaXform() 00038 * 00039 * Affine coordinate transformation 00040 * l_int32 getAffineXformCoeffs() 00041 * l_int32 affineInvertXform() 00042 * l_int32 affineXformSampledPt() 00043 * l_int32 affineXformPt() 00044 * 00045 * Interpolation helper functions 00046 * l_int32 linearInterpolatePixelGray() 00047 * l_int32 linearInterpolatePixelColor() 00048 * 00049 * Gauss-jordan linear equation solver 00050 * l_int32 gaussjordan() 00051 * 00052 * Affine image transformation using a sequence of 00053 * shear/scale/translation operations 00054 * PIX *pixAffineSequential() 00055 * 00056 * One can define a coordinate space by the location of the origin, 00057 * the orientation of x and y axes, and the unit scaling along 00058 * each axis. An affine transform is a general linear 00059 * transformation from one coordinate space to another. 00060 * 00061 * For the general case, we can define the affine transform using 00062 * two sets of three (noncollinear) points in a plane. One set 00063 * corresponds to the input (src) coordinate space; the other to the 00064 * transformed (dest) coordinate space. Each point in the 00065 * src corresponds to one of the points in the dest. With two 00066 * sets of three points, we get a set of 6 equations in 6 unknowns 00067 * that specifies the mapping between the coordinate spaces. 00068 * The interface here allows you to specify either the corresponding 00069 * sets of 3 points, or the transform itself (as a vector of 6 00070 * coefficients). 00071 * 00072 * Given the transform as a vector of 6 coefficients, we can compute 00073 * both a a pointwise affine coordinate transformation and an 00074 * affine image transformation. 00075 * 00076 * To compute the coordinate transform, we need the coordinate 00077 * value (x',y') in the transformed space for any point (x,y) 00078 * in the original space. To derive this transform from the 00079 * three corresponding points, it is convenient to express the affine 00080 * coordinate transformation using an LU decomposition of 00081 * a set of six linear equations that express the six coordinates 00082 * of the three points in the transformed space as a function of 00083 * the six coordinates in the original space. Once we have 00084 * this transform matrix , we can transform an image by 00085 * finding, for each destination pixel, the pixel (or pixels) 00086 * in the source that give rise to it. 00087 * 00088 * This 'pointwise' transformation can be done either by sampling 00089 * and picking a single pixel in the src to replicate into the dest, 00090 * or by interpolating (or averaging) over four src pixels to 00091 * determine the value of the dest pixel. The first method is 00092 * implemented by pixAffineSampled() and the second method by 00093 * pixAffine(). The interpolated method can only be used for 00094 * images with more than 1 bpp, but for these, the image quality 00095 * is significantly better than the sampled method, due to 00096 * the 'antialiasing' effect of weighting the src pixels. 00097 * 00098 * Interpolation works well when there is relatively little scaling, 00099 * or if there is image expansion in general. However, if there 00100 * is significant image reduction, one should apply a low-pass 00101 * filter before subsampling to avoid aliasing the high frequencies. 00102 * 00103 * A typical application might be to align two images, which 00104 * may be scaled, rotated and translated versions of each other. 00105 * Through some pre-processing, three corresponding points are 00106 * located in each of the two images. One of the images is 00107 * then to be (affine) transformed to align with the other. 00108 * As mentioned, the standard way to do this is to use three 00109 * sets of points, compute the 6 transformation coefficients 00110 * from these points that describe the linear transformation, 00111 * 00112 * x' = ax + by + c 00113 * y' = dx + ey + f 00114 * 00115 * and use this in a pointwise manner to transform the image. 00116 * 00117 * N.B. Be sure to see the comment in getAffineXformCoeffs(), 00118 * regarding using the inverse of the affine transform for points 00119 * to transform images. 00120 * 00121 * There is another way to do this transformation; namely, 00122 * by doing a sequence of simple affine transforms, without 00123 * computing directly the affine coordinate transformation. 00124 * We have at our disposal (1) translations (using rasterop), 00125 * (2) horizontal and vertical shear about any horizontal and vertical 00126 * line, respectively, and (3) non-isotropic scaling by two 00127 * arbitrary x and y scaling factors. We also have rotation 00128 * about an arbitrary point, but this is equivalent to a set 00129 * of three shears so we do not need to use it. 00130 * 00131 * Why might we do this? For binary images, it is usually 00132 * more efficient to do such transformations by a sequence 00133 * of word parallel operations. Shear and translation can be 00134 * done in-place and word parallel; arbitrary scaling is 00135 * mostly pixel-wise. 00136 * 00137 * Suppose that we are tranforming image 1 to correspond to image 2. 00138 * We have a set of three points, describing the coordinate space 00139 * embedded in image 1, and we need to transform image 1 until 00140 * those three points exactly correspond to the new coordinate space 00141 * defined by the second set of three points. In our image 00142 * matching application, the latter set of three points was 00143 * found to be the corresponding points in image 2. 00144 * 00145 * The most elegant way I can think of to do such a sequential 00146 * implementation is to imagine that we're going to transform 00147 * BOTH images until they're aligned. (We don't really want 00148 * to transform both, because in fact we may only have one image 00149 * that is undergoing a general affine transformation.) 00150 * 00151 * Choose the 3 corresponding points as follows: 00152 * - The 1st point is an origin 00153 * - The 2nd point gives the orientation and scaling of the 00154 * "x" axis with respect to the origin 00155 * - The 3rd point does likewise for the "y" axis. 00156 * These "axes" must not be collinear; otherwise they are 00157 * arbitrary (although some strange things will happen if 00158 * the handedness sweeping through the minimum angle between 00159 * the axes is opposite). 00160 * 00161 * An important constraint is that we have shear operations 00162 * about an arbitrary horizontal or vertical line, but always 00163 * parallel to the x or y axis. If we continue to pretend that 00164 * we have an unprimed coordinate space embedded in image 1 and 00165 * a primed coordinate space embedded in image 2, we imagine 00166 * (a) transforming image 1 by horizontal and vertical shears about 00167 * point 1 to align points 3 and 2 along the y and x axes, 00168 * respectively, and (b) transforming image 2 by horizontal and 00169 * vertical shears about point 1' to align points 3' and 2' along 00170 * the y and x axes. Then we scale image 1 so that the distances 00171 * from 1 to 2 and from 1 to 3 are equal to the distances in 00172 * image 2 from 1' to 2' and from 1' to 3'. This scaling operation 00173 * leaves the true image origin, at (0,0) invariant, and will in 00174 * general translate point 1. The original points 1 and 1' will 00175 * typically not coincide in any event, so we must translate 00176 * the origin of image 1, at its current point 1, to the origin 00177 * of image 2 at 1'. The images should now be aligned. But 00178 * because we never really transformed image 2 (and image 2 may 00179 * not even exist), we now perform on image 1 the reverse of 00180 * the shear transforms that we imagined doing on image 2; 00181 * namely, the negative vertical shear followed by the negative 00182 * horizontal shear. Image 1 should now have its transformed 00183 * unprimed coordinates aligned with the original primed 00184 * coordinates. In all this, it is only necessary to keep track 00185 * of the shear angles and translations of points during the shears. 00186 * What has been accomplished is a general affine transformation 00187 * on image 1. 00188 * 00189 * Having described all this, if you are going to use an 00190 * affine transformation in an application, this is what you 00191 * need to know: 00192 * 00193 * (1) You should NEVER use the sequential method, because 00194 * the image quality for 1 bpp text is much poorer 00195 * (even though it is about 2x faster than the pointwise sampled 00196 * method), and for images with depth greater than 1, it is 00197 * nearly 20x slower than the pointwise sampled method 00198 * and over 10x slower than the pointwise interpolated method! 00199 * The sequential method is given here for purely 00200 * pedagogical reasons. 00201 * 00202 * (2) For 1 bpp images, use the pointwise sampled function 00203 * pixAffineSampled(). For all other images, the best 00204 * quality results result from using the pointwise 00205 * interpolated function pixAffinePta() or pixAffine(); 00206 * the cost is less than a doubling of the computation time 00207 * with respect to the sampled function. If you use 00208 * interpolation on colormapped images, the colormap will 00209 * be removed, resulting in either a grayscale or color 00210 * image, depending on the values in the colormap. 00211 * If you want to retain the colormap, use pixAffineSampled(). 00212 * 00213 * Typical relative timing of pointwise transforms (sampled = 1.0): 00214 * 8 bpp: sampled 1.0 00215 * interpolated 1.6 00216 * 32 bpp: sampled 1.0 00217 * interpolated 1.8 00218 * Additionally, the computation time/pixel is nearly the same 00219 * for 8 bpp and 32 bpp, for both sampled and interpolated. 00220 */ 00221 00222 00223 #include <stdio.h> 00224 #include <stdlib.h> 00225 #include <string.h> 00226 #include <math.h> 00227 #include "allheaders.h" 00228 00229 extern l_float32 AlphaMaskBorderVals[2]; 00230 00231 #ifndef NO_CONSOLE_IO 00232 #define DEBUG 0 00233 #endif /* ~NO_CONSOLE_IO */ 00234 00235 00236 /*-------------------------------------------------------------* 00237 * Sampled affine image transformation * 00238 *-------------------------------------------------------------*/ 00239 /*! 00240 * pixAffineSampledPta() 00241 * 00242 * Input: pixs (all depths) 00243 * ptad (3 pts of final coordinate space) 00244 * ptas (3 pts of initial coordinate space) 00245 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00246 * Return: pixd, or null on error 00247 * 00248 * Notes: 00249 * (1) Brings in either black or white pixels from the boundary. 00250 * (2) Retains colormap, which you can do for a sampled transform.. 00251 * (3) The 3 points must not be collinear. 00252 * (4) The order of the 3 points is arbitrary; however, to compare 00253 * with the sequential transform they must be in these locations 00254 * and in this order: origin, x-axis, y-axis. 00255 * (5) For 1 bpp images, this has much better quality results 00256 * than pixAffineSequential(), particularly for text. 00257 * It is about 3x slower, but does not require additional 00258 * border pixels. The poor quality of pixAffineSequential() 00259 * is due to repeated quantized transforms. It is strongly 00260 * recommended that pixAffineSampled() be used for 1 bpp images. 00261 * (6) For 8 or 32 bpp, much better quality is obtained by the 00262 * somewhat slower pixAffinePta(). See that function 00263 * for relative timings between sampled and interpolated. 00264 * (7) To repeat, use of the sequential transform, 00265 * pixAffineSequential(), for any images, is discouraged. 00266 */ 00267 PIX * 00268 pixAffineSampledPta(PIX *pixs, 00269 PTA *ptad, 00270 PTA *ptas, 00271 l_int32 incolor) 00272 { 00273 l_float32 *vc; 00274 PIX *pixd; 00275 00276 PROCNAME("pixAffineSampledPta"); 00277 00278 if (!pixs) 00279 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00280 if (!ptas) 00281 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00282 if (!ptad) 00283 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00284 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00285 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00286 if (ptaGetCount(ptas) != 3) 00287 return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); 00288 if (ptaGetCount(ptad) != 3) 00289 return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); 00290 00291 /* Get backwards transform from dest to src, and apply it */ 00292 getAffineXformCoeffs(ptad, ptas, &vc); 00293 pixd = pixAffineSampled(pixs, vc, incolor); 00294 FREE(vc); 00295 00296 return pixd; 00297 } 00298 00299 00300 /*! 00301 * pixAffineSampled() 00302 * 00303 * Input: pixs (all depths) 00304 * vc (vector of 6 coefficients for affine transformation) 00305 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00306 * Return: pixd, or null on error 00307 * 00308 * Notes: 00309 * (1) Brings in either black or white pixels from the boundary. 00310 * (2) Retains colormap, which you can do for a sampled transform.. 00311 * (3) For 8 or 32 bpp, much better quality is obtained by the 00312 * somewhat slower pixAffine(). See that function 00313 * for relative timings between sampled and interpolated. 00314 */ 00315 PIX * 00316 pixAffineSampled(PIX *pixs, 00317 l_float32 *vc, 00318 l_int32 incolor) 00319 { 00320 l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; 00321 l_uint32 val; 00322 l_uint32 *datas, *datad, *lines, *lined; 00323 PIX *pixd; 00324 PIXCMAP *cmap; 00325 00326 PROCNAME("pixAffineSampled"); 00327 00328 if (!pixs) 00329 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00330 if (!vc) 00331 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00332 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00333 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00334 pixGetDimensions(pixs, &w, &h, &d); 00335 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) 00336 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); 00337 00338 /* Init all dest pixels to color to be brought in from outside */ 00339 pixd = pixCreateTemplate(pixs); 00340 if ((cmap = pixGetColormap(pixs)) != NULL) { 00341 if (incolor == L_BRING_IN_WHITE) 00342 color = 1; 00343 else 00344 color = 0; 00345 pixcmapAddBlackOrWhite(cmap, color, &cmapindex); 00346 pixSetAllArbitrary(pixd, cmapindex); 00347 } 00348 else { 00349 if ((d == 1 && incolor == L_BRING_IN_WHITE) || 00350 (d > 1 && incolor == L_BRING_IN_BLACK)) 00351 pixClearAll(pixd); 00352 else 00353 pixSetAll(pixd); 00354 } 00355 00356 /* Scan over the dest pixels */ 00357 datas = pixGetData(pixs); 00358 wpls = pixGetWpl(pixs); 00359 datad = pixGetData(pixd); 00360 wpld = pixGetWpl(pixd); 00361 for (i = 0; i < h; i++) { 00362 lined = datad + i * wpld; 00363 for (j = 0; j < w; j++) { 00364 affineXformSampledPt(vc, j, i, &x, &y); 00365 if (x < 0 || y < 0 || x >=w || y >= h) 00366 continue; 00367 lines = datas + y * wpls; 00368 if (d == 1) { 00369 val = GET_DATA_BIT(lines, x); 00370 SET_DATA_BIT_VAL(lined, j, val); 00371 } 00372 else if (d == 8) { 00373 val = GET_DATA_BYTE(lines, x); 00374 SET_DATA_BYTE(lined, j, val); 00375 } 00376 else if (d == 32) { 00377 lined[j] = lines[x]; 00378 } 00379 else if (d == 2) { 00380 val = GET_DATA_DIBIT(lines, x); 00381 SET_DATA_DIBIT(lined, j, val); 00382 } 00383 else if (d == 4) { 00384 val = GET_DATA_QBIT(lines, x); 00385 SET_DATA_QBIT(lined, j, val); 00386 } 00387 } 00388 } 00389 00390 return pixd; 00391 } 00392 00393 00394 /*---------------------------------------------------------------------* 00395 * Interpolated affine image transformation * 00396 *---------------------------------------------------------------------*/ 00397 /*! 00398 * pixAffinePta() 00399 * 00400 * Input: pixs (all depths; colormap ok) 00401 * ptad (3 pts of final coordinate space) 00402 * ptas (3 pts of initial coordinate space) 00403 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00404 * Return: pixd, or null on error 00405 * 00406 * Notes: 00407 * (1) Brings in either black or white pixels from the boundary 00408 * (2) Removes any existing colormap, if necessary, before transforming 00409 */ 00410 PIX * 00411 pixAffinePta(PIX *pixs, 00412 PTA *ptad, 00413 PTA *ptas, 00414 l_int32 incolor) 00415 { 00416 l_int32 d; 00417 l_uint32 colorval; 00418 PIX *pixt1, *pixt2, *pixd; 00419 00420 PROCNAME("pixAffinePta"); 00421 00422 if (!pixs) 00423 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00424 if (!ptas) 00425 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00426 if (!ptad) 00427 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00428 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00429 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00430 if (ptaGetCount(ptas) != 3) 00431 return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); 00432 if (ptaGetCount(ptad) != 3) 00433 return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); 00434 00435 if (pixGetDepth(pixs) == 1) 00436 return pixAffineSampledPta(pixs, ptad, ptas, incolor); 00437 00438 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ 00439 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00440 d = pixGetDepth(pixt1); 00441 if (d < 8) 00442 pixt2 = pixConvertTo8(pixt1, FALSE); 00443 else 00444 pixt2 = pixClone(pixt1); 00445 d = pixGetDepth(pixt2); 00446 00447 /* Compute actual color to bring in from edges */ 00448 colorval = 0; 00449 if (incolor == L_BRING_IN_WHITE) { 00450 if (d == 8) 00451 colorval = 255; 00452 else /* d == 32 */ 00453 colorval = 0xffffff00; 00454 } 00455 00456 if (d == 8) 00457 pixd = pixAffinePtaGray(pixt2, ptad, ptas, colorval); 00458 else /* d == 32 */ 00459 pixd = pixAffinePtaColor(pixt2, ptad, ptas, colorval); 00460 pixDestroy(&pixt1); 00461 pixDestroy(&pixt2); 00462 return pixd; 00463 } 00464 00465 00466 /*! 00467 * pixAffine() 00468 * 00469 * Input: pixs (all depths; colormap ok) 00470 * vc (vector of 6 coefficients for affine transformation) 00471 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00472 * Return: pixd, or null on error 00473 * 00474 * Notes: 00475 * (1) Brings in either black or white pixels from the boundary 00476 * (2) Removes any existing colormap, if necessary, before transforming 00477 */ 00478 PIX * 00479 pixAffine(PIX *pixs, 00480 l_float32 *vc, 00481 l_int32 incolor) 00482 { 00483 l_int32 d; 00484 l_uint32 colorval; 00485 PIX *pixt1, *pixt2, *pixd; 00486 00487 PROCNAME("pixAffine"); 00488 00489 if (!pixs) 00490 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00491 if (!vc) 00492 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00493 00494 if (pixGetDepth(pixs) == 1) 00495 return pixAffineSampled(pixs, vc, incolor); 00496 00497 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ 00498 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00499 d = pixGetDepth(pixt1); 00500 if (d < 8) 00501 pixt2 = pixConvertTo8(pixt1, FALSE); 00502 else 00503 pixt2 = pixClone(pixt1); 00504 d = pixGetDepth(pixt2); 00505 00506 /* Compute actual color to bring in from edges */ 00507 colorval = 0; 00508 if (incolor == L_BRING_IN_WHITE) { 00509 if (d == 8) 00510 colorval = 255; 00511 else /* d == 32 */ 00512 colorval = 0xffffff00; 00513 } 00514 00515 if (d == 8) 00516 pixd = pixAffineGray(pixt2, vc, colorval); 00517 else /* d == 32 */ 00518 pixd = pixAffineColor(pixt2, vc, colorval); 00519 pixDestroy(&pixt1); 00520 pixDestroy(&pixt2); 00521 return pixd; 00522 } 00523 00524 00525 /*! 00526 * pixAffinePtaColor() 00527 * 00528 * Input: pixs (32 bpp) 00529 * ptad (3 pts of final coordinate space) 00530 * ptas (3 pts of initial coordinate space) 00531 * colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE) 00532 * Return: pixd, or null on error 00533 */ 00534 PIX * 00535 pixAffinePtaColor(PIX *pixs, 00536 PTA *ptad, 00537 PTA *ptas, 00538 l_uint32 colorval) 00539 { 00540 l_float32 *vc; 00541 PIX *pixd; 00542 00543 PROCNAME("pixAffinePtaColor"); 00544 00545 if (!pixs) 00546 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00547 if (!ptas) 00548 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00549 if (!ptad) 00550 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00551 if (pixGetDepth(pixs) != 32) 00552 return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); 00553 if (ptaGetCount(ptas) != 3) 00554 return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); 00555 if (ptaGetCount(ptad) != 3) 00556 return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); 00557 00558 /* Get backwards transform from dest to src, and apply it */ 00559 getAffineXformCoeffs(ptad, ptas, &vc); 00560 pixd = pixAffineColor(pixs, vc, colorval); 00561 FREE(vc); 00562 00563 return pixd; 00564 } 00565 00566 00567 /*! 00568 * pixAffineColor() 00569 * 00570 * Input: pixs (32 bpp) 00571 * vc (vector of 6 coefficients for affine transformation) 00572 * colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE) 00573 * Return: pixd, or null on error 00574 */ 00575 PIX * 00576 pixAffineColor(PIX *pixs, 00577 l_float32 *vc, 00578 l_uint32 colorval) 00579 { 00580 l_int32 i, j, w, h, d, wpls, wpld; 00581 l_uint32 val; 00582 l_uint32 *datas, *datad, *lined; 00583 l_float32 x, y; 00584 PIX *pixd; 00585 00586 PROCNAME("pixAffineColor"); 00587 00588 if (!pixs) 00589 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00590 pixGetDimensions(pixs, &w, &h, &d); 00591 if (d != 32) 00592 return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); 00593 if (!vc) 00594 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00595 00596 datas = pixGetData(pixs); 00597 wpls = pixGetWpl(pixs); 00598 pixd = pixCreateTemplate(pixs); 00599 pixSetAllArbitrary(pixd, colorval); 00600 datad = pixGetData(pixd); 00601 wpld = pixGetWpl(pixd); 00602 00603 /* Iterate over destination pixels */ 00604 for (i = 0; i < h; i++) { 00605 lined = datad + i * wpld; 00606 for (j = 0; j < w; j++) { 00607 /* Compute float src pixel location corresponding to (i,j) */ 00608 affineXformPt(vc, j, i, &x, &y); 00609 linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, 00610 &val); 00611 *(lined + j) = val; 00612 } 00613 } 00614 00615 return pixd; 00616 } 00617 00618 00619 /*! 00620 * pixAffinePtaGray() 00621 * 00622 * Input: pixs (8 bpp) 00623 * ptad (3 pts of final coordinate space) 00624 * ptas (3 pts of initial coordinate space) 00625 * grayval (0 to bring in BLACK, 255 for WHITE) 00626 * Return: pixd, or null on error 00627 */ 00628 PIX * 00629 pixAffinePtaGray(PIX *pixs, 00630 PTA *ptad, 00631 PTA *ptas, 00632 l_uint8 grayval) 00633 { 00634 l_float32 *vc; 00635 PIX *pixd; 00636 00637 PROCNAME("pixAffinePtaGray"); 00638 00639 if (!pixs) 00640 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00641 if (!ptas) 00642 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00643 if (!ptad) 00644 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00645 if (pixGetDepth(pixs) != 8) 00646 return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); 00647 if (ptaGetCount(ptas) != 3) 00648 return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); 00649 if (ptaGetCount(ptad) != 3) 00650 return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); 00651 00652 /* Get backwards transform from dest to src, and apply it */ 00653 getAffineXformCoeffs(ptad, ptas, &vc); 00654 pixd = pixAffineGray(pixs, vc, grayval); 00655 FREE(vc); 00656 00657 return pixd; 00658 } 00659 00660 00661 00662 /*! 00663 * pixAffineGray() 00664 * 00665 * Input: pixs (8 bpp) 00666 * vc (vector of 6 coefficients for affine transformation) 00667 * grayval (0 to bring in BLACK, 255 for WHITE) 00668 * Return: pixd, or null on error 00669 */ 00670 PIX * 00671 pixAffineGray(PIX *pixs, 00672 l_float32 *vc, 00673 l_uint8 grayval) 00674 { 00675 l_int32 i, j, w, h, wpls, wpld, val; 00676 l_uint32 *datas, *datad, *lined; 00677 l_float32 x, y; 00678 PIX *pixd; 00679 00680 PROCNAME("pixAffineGray"); 00681 00682 if (!pixs) 00683 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00684 pixGetDimensions(pixs, &w, &h, NULL); 00685 if (pixGetDepth(pixs) != 8) 00686 return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); 00687 if (!vc) 00688 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00689 00690 datas = pixGetData(pixs); 00691 wpls = pixGetWpl(pixs); 00692 pixd = pixCreateTemplate(pixs); 00693 pixSetAllArbitrary(pixd, grayval); 00694 datad = pixGetData(pixd); 00695 wpld = pixGetWpl(pixd); 00696 00697 /* Iterate over destination pixels */ 00698 for (i = 0; i < h; i++) { 00699 lined = datad + i * wpld; 00700 for (j = 0; j < w; j++) { 00701 /* Compute float src pixel location corresponding to (i,j) */ 00702 affineXformPt(vc, j, i, &x, &y); 00703 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); 00704 SET_DATA_BYTE(lined, j, val); 00705 } 00706 } 00707 00708 return pixd; 00709 } 00710 00711 00712 /*---------------------------------------------------------------------------* 00713 * Affine transform including alpha (blend) component and gamma transform * 00714 *---------------------------------------------------------------------------*/ 00715 /*! 00716 * pixAffinePtaWithAlpha() 00717 * 00718 * Input: pixs (32 bpp rgb) 00719 * ptad (3 pts of final coordinate space) 00720 * ptas (3 pts of initial coordinate space) 00721 * pixg (<optional> 8 bpp, can be null) 00722 * fract (between 0.0 and 1.0, with 0.0 fully transparent 00723 * and 1.0 fully opaque) 00724 * border (of pixels added to capture transformed source pixels) 00725 * Return: pixd, or null on error 00726 * 00727 * Notes: 00728 * (1) The alpha channel is transformed separately from pixs, 00729 * and aligns with it, being fully transparent outside the 00730 * boundary of the transformed pixs. For pixels that are fully 00731 * transparent, a blending function like pixBlendWithGrayMask() 00732 * will give zero weight to corresponding pixels in pixs. 00733 * (2) If pixg is NULL, it is generated as an alpha layer that is 00734 * partially opaque, using @fract. Otherwise, it is cropped 00735 * to pixs if required and @fract is ignored. The alpha channel 00736 * in pixs is never used. 00737 * (3) Colormaps are removed. 00738 * (4) When pixs is transformed, it doesn't matter what color is brought 00739 * in because the alpha channel will be transparent (0) there. 00740 * (5) To avoid losing source pixels in the destination, it may be 00741 * necessary to add a border to the source pix before doing 00742 * the affine transformation. This can be any non-negative number. 00743 * (6) The input @ptad and @ptas are in a coordinate space before 00744 * the border is added. Internally, we compensate for this 00745 * before doing the affine transform on the image after the border 00746 * is added. 00747 * (7) The default setting for the border values in the alpha channel 00748 * is 0 (transparent) for the outermost ring of pixels and 00749 * (0.5 * fract * 255) for the second ring. When blended over 00750 * a second image, this 00751 * (a) shrinks the visible image to make a clean overlap edge 00752 * with an image below, and 00753 * (b) softens the edges by weakening the aliasing there. 00754 * Use l_setAlphaMaskBorder() to change these values. 00755 */ 00756 PIX * 00757 pixAffinePtaWithAlpha(PIX *pixs, 00758 PTA *ptad, 00759 PTA *ptas, 00760 PIX *pixg, 00761 l_float32 fract, 00762 l_int32 border) 00763 { 00764 l_int32 ws, hs, d; 00765 PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; 00766 PTA *ptad2, *ptas2; 00767 00768 PROCNAME("pixAffinePtaWithAlpha"); 00769 00770 if (!pixs) 00771 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00772 pixGetDimensions(pixs, &ws, &hs, &d); 00773 if (d != 32 && pixGetColormap(pixs) == NULL) 00774 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 00775 if (pixg && pixGetDepth(pixg) != 8) { 00776 L_WARNING("pixg not 8 bpp; using @fract transparent alpha", procName); 00777 pixg = NULL; 00778 } 00779 if (!pixg && (fract < 0.0 || fract > 1.0)) { 00780 L_WARNING("invalid fract; using 1.0 (fully transparent)", procName); 00781 fract = 1.0; 00782 } 00783 if (!pixg && fract == 0.0) 00784 L_WARNING("fully opaque alpha; image will not be blended", procName); 00785 if (!ptad) 00786 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00787 if (!ptas) 00788 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00789 00790 /* Add border; the color doesn't matter */ 00791 pixb1 = pixAddBorder(pixs, border, 0); 00792 00793 /* Transform the ptr arrays to work on the bordered image */ 00794 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); 00795 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); 00796 00797 /* Do separate affine transform of rgb channels of pixs and of pixg */ 00798 pixd = pixAffinePtaColor(pixb1, ptad2, ptas2, 0); 00799 if (!pixg) { 00800 pixg2 = pixCreate(ws, hs, 8); 00801 if (fract == 1.0) 00802 pixSetAll(pixg2); 00803 else 00804 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); 00805 } 00806 else 00807 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); 00808 if (ws > 10 && hs > 10) { /* see note 7 */ 00809 pixSetBorderRingVal(pixg2, 1, 00810 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); 00811 pixSetBorderRingVal(pixg2, 2, 00812 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); 00813 00814 } 00815 pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ 00816 pixga = pixAffinePtaGray(pixb2, ptad2, ptas2, 0); 00817 pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); 00818 00819 pixDestroy(&pixg2); 00820 pixDestroy(&pixb1); 00821 pixDestroy(&pixb2); 00822 pixDestroy(&pixga); 00823 ptaDestroy(&ptad2); 00824 ptaDestroy(&ptas2); 00825 return pixd; 00826 } 00827 00828 00829 /*! 00830 * pixAffinePtaGammaXform() 00831 * 00832 * Input: pixs (32 bpp rgb) 00833 * gamma (gamma correction; must be > 0.0) 00834 * ptad (3 pts of final coordinate space) 00835 * ptas (3 pts of initial coordinate space) 00836 * fract (between 0.0 and 1.0, with 1.0 fully transparent) 00837 * border (of pixels to capture transformed source pixels) 00838 * Return: pixd, or null on error 00839 * 00840 * Notes: 00841 * (1) This wraps a gamma/inverse-gamma photometric transform around 00842 * pixAffinePtaWithAlpha(). 00843 * (2) For usage, see notes in pixAffinePtaWithAlpha() and 00844 * pixGammaTRCWithAlpha(). 00845 * (3) The basic idea of a gamma/inverse-gamma transform is to remove 00846 * any gamma correction before the affine transform, and restore 00847 * it afterward. The effects can be subtle, but important for 00848 * some applications. For example, using gamma > 1.0 will 00849 * cause the dark areas to become somewhat lighter and slightly 00850 * reduce aliasing effects when blending using the alpha channel. 00851 */ 00852 PIX * 00853 pixAffinePtaGammaXform(PIX *pixs, 00854 l_float32 gamma, 00855 PTA *ptad, 00856 PTA *ptas, 00857 l_float32 fract, 00858 l_int32 border) 00859 { 00860 PIX *pixg, *pixd; 00861 00862 PROCNAME("pixAffinePtaGammaXform"); 00863 00864 if (!pixs || (pixGetDepth(pixs) != 32)) 00865 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); 00866 if (fract == 0.0) 00867 L_WARNING("fully opaque alpha; image cannot be blended", procName); 00868 if (gamma <= 0.0) { 00869 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00870 gamma = 1.0; 00871 } 00872 00873 pixg = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255); 00874 pixd = pixAffinePtaWithAlpha(pixg, ptad, ptas, NULL, fract, border); 00875 pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255); 00876 pixDestroy(&pixg); 00877 return pixd; 00878 } 00879 00880 00881 /*-------------------------------------------------------------* 00882 * Affine coordinate transformation * 00883 *-------------------------------------------------------------*/ 00884 /*! 00885 * getAffineXformCoeffs() 00886 * 00887 * Input: ptas (source 3 points; unprimed) 00888 * ptad (transformed 3 points; primed) 00889 * &vc (<return> vector of coefficients of transform) 00890 * Return: 0 if OK; 1 on error 00891 * 00892 * We have a set of six equations, describing the affine 00893 * transformation that takes 3 points (ptas) into 3 other 00894 * points (ptad). These equations are: 00895 * 00896 * x1' = c[0]*x1 + c[1]*y1 + c[2] 00897 * y1' = c[3]*x1 + c[4]*y1 + c[5] 00898 * x2' = c[0]*x2 + c[1]*y2 + c[2] 00899 * y2' = c[3]*x2 + c[4]*y2 + c[5] 00900 * x3' = c[0]*x3 + c[1]*y3 + c[2] 00901 * y3' = c[3]*x3 + c[4]*y3 + c[5] 00902 * 00903 * This can be represented as 00904 * 00905 * AC = B 00906 * 00907 * where B and C are column vectors 00908 * 00909 * B = [ x1' y1' x2' y2' x3' y3' ] 00910 * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] ] 00911 * 00912 * and A is the 6x6 matrix 00913 * 00914 * x1 y1 1 0 0 0 00915 * 0 0 0 x1 y1 1 00916 * x2 y2 1 0 0 0 00917 * 0 0 0 x2 y2 1 00918 * x3 y3 1 0 0 0 00919 * 0 0 0 x3 y3 1 00920 * 00921 * These six equations are solved here for the coefficients C. 00922 * 00923 * These six coefficients can then be used to find the dest 00924 * point (x',y') corresponding to any src point (x,y), according 00925 * to the equations 00926 * 00927 * x' = c[0]x + c[1]y + c[2] 00928 * y' = c[3]x + c[4]y + c[5] 00929 * 00930 * that are implemented in affineXformPt(). 00931 * 00932 * !!!!!!!!!!!!!!!!!! Very important !!!!!!!!!!!!!!!!!!!!!! 00933 * 00934 * When the affine transform is composed from a set of simple 00935 * operations such as translation, scaling and rotation, 00936 * it is built in a form to convert from the un-transformed src 00937 * point to the transformed dest point. However, when an 00938 * affine transform is used on images, it is used in an inverted 00939 * way: it converts from the transformed dest point to the 00940 * un-transformed src point. So, for example, if you transform 00941 * a boxa using transform A, to transform an image in the same 00942 * way you must use the inverse of A. 00943 * 00944 * For example, if you transform a boxa with a 3x3 affine matrix 00945 * 'mat', the analogous image transformation must use 'matinv': 00946 * 00947 * boxad = boxaAffineTransform(boxas, mat); 00948 * affineInvertXform(mat, &matinv); 00949 * pixd = pixAffine(pixs, matinv, L_BRING_IN_WHITE); 00950 * 00951 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 00952 */ 00953 l_int32 00954 getAffineXformCoeffs(PTA *ptas, 00955 PTA *ptad, 00956 l_float32 **pvc) 00957 { 00958 l_int32 i; 00959 l_float32 x1, y1, x2, y2, x3, y3; 00960 l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ 00961 l_float32 *a[6]; /* 6x6 matrix A */ 00962 00963 PROCNAME("getAffineXformCoeffs"); 00964 00965 if (!ptas) 00966 return ERROR_INT("ptas not defined", procName, 1); 00967 if (!ptad) 00968 return ERROR_INT("ptad not defined", procName, 1); 00969 if (!pvc) 00970 return ERROR_INT("&vc not defined", procName, 1); 00971 00972 if ((b = (l_float32 *)CALLOC(6, sizeof(l_float32))) == NULL) 00973 return ERROR_INT("b not made", procName, 1); 00974 *pvc = b; 00975 00976 ptaGetPt(ptas, 0, &x1, &y1); 00977 ptaGetPt(ptas, 1, &x2, &y2); 00978 ptaGetPt(ptas, 2, &x3, &y3); 00979 ptaGetPt(ptad, 0, &b[0], &b[1]); 00980 ptaGetPt(ptad, 1, &b[2], &b[3]); 00981 ptaGetPt(ptad, 2, &b[4], &b[5]); 00982 00983 for (i = 0; i < 6; i++) 00984 if ((a[i] = (l_float32 *)CALLOC(6, sizeof(l_float32))) == NULL) 00985 return ERROR_INT("a[i] not made", procName, 1); 00986 00987 a[0][0] = x1; 00988 a[0][1] = y1; 00989 a[0][2] = 1.; 00990 a[1][3] = x1; 00991 a[1][4] = y1; 00992 a[1][5] = 1.; 00993 a[2][0] = x2; 00994 a[2][1] = y2; 00995 a[2][2] = 1.; 00996 a[3][3] = x2; 00997 a[3][4] = y2; 00998 a[3][5] = 1.; 00999 a[4][0] = x3; 01000 a[4][1] = y3; 01001 a[4][2] = 1.; 01002 a[5][3] = x3; 01003 a[5][4] = y3; 01004 a[5][5] = 1.; 01005 01006 gaussjordan(a, b, 6); 01007 01008 for (i = 0; i < 6; i++) 01009 FREE(a[i]); 01010 01011 return 0; 01012 } 01013 01014 01015 /*! 01016 * affineInvertXform() 01017 * 01018 * Input: vc (vector of 6 coefficients) 01019 * *vci (<return> inverted transform) 01020 * Return: 0 if OK; 1 on error 01021 * 01022 * Notes: 01023 * (1) The 6 affine transform coefficients are the first 01024 * two rows of a 3x3 matrix where the last row has 01025 * only a 1 in the third column. We invert this 01026 * using gaussjordan(), and select the first 2 rows 01027 * as the coefficients of the inverse affine transform. 01028 * (2) Alternatively, we can find the inverse transform 01029 * coefficients by inverting the 2x2 submatrix, 01030 * and treating the top 2 coefficients in the 3rd column as 01031 * a RHS vector for that 2x2 submatrix. Then the 01032 * 6 inverted transform coefficients are composed of 01033 * the inverted 2x2 submatrix and the negative of the 01034 * transformed RHS vector. Why is this so? We have 01035 * Y = AX + R (2 equations in 6 unknowns) 01036 * Then 01037 * X = A'Y - A'R 01038 * Gauss-jordan solves 01039 * AF = R 01040 * and puts the solution for F, which is A'R, 01041 * into the input R vector. 01042 * 01043 */ 01044 l_int32 01045 affineInvertXform(l_float32 *vc, 01046 l_float32 **pvci) 01047 { 01048 l_int32 i; 01049 l_float32 *vci; 01050 l_float32 *a[3]; 01051 l_float32 b[3] = {1.0, 1.0, 1.0}; /* anything; results ignored */ 01052 01053 PROCNAME("affineInvertXform"); 01054 01055 if (!pvci) 01056 return ERROR_INT("&vci not defined", procName, 1); 01057 *pvci = NULL; 01058 if (!vc) 01059 return ERROR_INT("vc not defined", procName, 1); 01060 01061 for (i = 0; i < 3; i++) 01062 a[i] = (l_float32 *)CALLOC(3, sizeof(l_float32)); 01063 a[0][0] = vc[0]; 01064 a[0][1] = vc[1]; 01065 a[0][2] = vc[2]; 01066 a[1][0] = vc[3]; 01067 a[1][1] = vc[4]; 01068 a[1][2] = vc[5]; 01069 a[2][2] = 1.0; 01070 gaussjordan(a, b, 3); /* now matrix a contains the inverse */ 01071 vci = (l_float32 *)CALLOC(6, sizeof(l_float32)); 01072 *pvci = vci; 01073 vci[0] = a[0][0]; 01074 vci[1] = a[0][1]; 01075 vci[2] = a[0][2]; 01076 vci[3] = a[1][0]; 01077 vci[4] = a[1][1]; 01078 vci[5] = a[1][2]; 01079 01080 #if 0 01081 /* Alternative version, inverting a 2x2 matrix */ 01082 for (i = 0; i < 2; i++) 01083 a[i] = (l_float32 *)CALLOC(2, sizeof(l_float32)); 01084 a[0][0] = vc[0]; 01085 a[0][1] = vc[1]; 01086 a[1][0] = vc[3]; 01087 a[1][1] = vc[4]; 01088 b[0] = vc[2]; 01089 b[1] = vc[5]; 01090 gaussjordan(a, b, 2); /* now matrix a contains the inverse */ 01091 vci = (l_float32 *)CALLOC(6, sizeof(l_float32)); 01092 *pvci = vci; 01093 vci[0] = a[0][0]; 01094 vci[1] = a[0][1]; 01095 vci[2] = -b[0]; /* note sign */ 01096 vci[3] = a[1][0]; 01097 vci[4] = a[1][1]; 01098 vci[5] = -b[1]; /* note sign */ 01099 #endif 01100 01101 return 0; 01102 } 01103 01104 01105 /*! 01106 * affineXformSampledPt() 01107 * 01108 * Input: vc (vector of 6 coefficients) 01109 * (x, y) (initial point) 01110 * (&xp, &yp) (<return> transformed point) 01111 * Return: 0 if OK; 1 on error 01112 * 01113 * Notes: 01114 * (1) This finds the nearest pixel coordinates of the transformed point. 01115 * (2) It does not check ptrs for returned data! 01116 */ 01117 l_int32 01118 affineXformSampledPt(l_float32 *vc, 01119 l_int32 x, 01120 l_int32 y, 01121 l_int32 *pxp, 01122 l_int32 *pyp) 01123 { 01124 PROCNAME("affineXformSampledPt"); 01125 01126 if (!vc) 01127 return ERROR_INT("vc not defined", procName, 1); 01128 01129 *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] + 0.5); 01130 *pyp = (l_int32)(vc[3] * x + vc[4] * y + vc[5] + 0.5); 01131 return 0; 01132 } 01133 01134 01135 /*! 01136 * affineXformPt() 01137 * 01138 * Input: vc (vector of 6 coefficients) 01139 * (x, y) (initial point) 01140 * (&xp, &yp) (<return> transformed point) 01141 * Return: 0 if OK; 1 on error 01142 * 01143 * Notes: 01144 * (1) This computes the floating point location of the transformed point. 01145 * (2) It does not check ptrs for returned data! 01146 */ 01147 l_int32 01148 affineXformPt(l_float32 *vc, 01149 l_int32 x, 01150 l_int32 y, 01151 l_float32 *pxp, 01152 l_float32 *pyp) 01153 { 01154 PROCNAME("affineXformPt"); 01155 01156 if (!vc) 01157 return ERROR_INT("vc not defined", procName, 1); 01158 01159 *pxp = vc[0] * x + vc[1] * y + vc[2]; 01160 *pyp = vc[3] * x + vc[4] * y + vc[5]; 01161 return 0; 01162 } 01163 01164 01165 /*-------------------------------------------------------------* 01166 * Interpolation helper functions * 01167 *-------------------------------------------------------------*/ 01168 /*! 01169 * linearInterpolatePixelColor() 01170 * 01171 * Input: datas (ptr to beginning of image data) 01172 * wpls (32-bit word/line for this data array) 01173 * w, h (of image) 01174 * x, y (floating pt location for evaluation) 01175 * colorval (color brought in from the outside when the 01176 * input x,y location is outside the image; 01177 * in 0xrrggbb00 format)) 01178 * &val (<return> interpolated color value) 01179 * Return: 0 if OK, 1 on error 01180 * 01181 * Notes: 01182 * (1) This is a standard linear interpolation function. It is 01183 * equivalent to area weighting on each component, and 01184 * avoids "jaggies" when rendering sharp edges. 01185 */ 01186 l_int32 01187 linearInterpolatePixelColor(l_uint32 *datas, 01188 l_int32 wpls, 01189 l_int32 w, 01190 l_int32 h, 01191 l_float32 x, 01192 l_float32 y, 01193 l_uint32 colorval, 01194 l_uint32 *pval) 01195 { 01196 l_int32 xpm, ypm, xp, yp, xf, yf; 01197 l_int32 rval, gval, bval; 01198 l_uint32 word00, word01, word10, word11; 01199 l_uint32 *lines; 01200 01201 PROCNAME("linearInterpolatePixelColor"); 01202 01203 if (!pval) 01204 return ERROR_INT("&val not defined", procName, 1); 01205 *pval = colorval; 01206 if (!datas) 01207 return ERROR_INT("datas not defined", procName, 1); 01208 01209 /* Skip if off the edge */ 01210 if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0) 01211 return 0; 01212 01213 xpm = (l_int32)(16.0 * x + 0.5); 01214 ypm = (l_int32)(16.0 * y + 0.5); 01215 xp = xpm >> 4; 01216 yp = ypm >> 4; 01217 xf = xpm & 0x0f; 01218 yf = ypm & 0x0f; 01219 01220 #if DEBUG 01221 if (xf < 0 || yf < 0) 01222 fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); 01223 #endif /* DEBUG */ 01224 01225 /* Do area weighting (eqiv. to linear interpolation) */ 01226 lines = datas + yp * wpls; 01227 word00 = *(lines + xp); 01228 word10 = *(lines + xp + 1); 01229 word01 = *(lines + wpls + xp); 01230 word11 = *(lines + wpls + xp + 1); 01231 rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) + 01232 xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) + 01233 (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) + 01234 xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256; 01235 gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) + 01236 xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) + 01237 (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) + 01238 xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256; 01239 bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) + 01240 xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) + 01241 (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) + 01242 xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256; 01243 *pval = (rval << L_RED_SHIFT) | (gval << L_GREEN_SHIFT) | 01244 (bval << L_BLUE_SHIFT); 01245 return 0; 01246 } 01247 01248 01249 /*! 01250 * linearInterpolatePixelGray() 01251 * 01252 * Input: datas (ptr to beginning of image data) 01253 * wpls (32-bit word/line for this data array) 01254 * w, h (of image) 01255 * x, y (floating pt location for evaluation) 01256 * grayval (color brought in from the outside when the 01257 * input x,y location is outside the image) 01258 * &val (<return> interpolated gray value) 01259 * Return: 0 if OK, 1 on error 01260 * 01261 * Notes: 01262 * (1) This is a standard linear interpolation function. It is 01263 * equivalent to area weighting on each component, and 01264 * avoids "jaggies" when rendering sharp edges. 01265 */ 01266 l_int32 01267 linearInterpolatePixelGray(l_uint32 *datas, 01268 l_int32 wpls, 01269 l_int32 w, 01270 l_int32 h, 01271 l_float32 x, 01272 l_float32 y, 01273 l_int32 grayval, 01274 l_int32 *pval) 01275 { 01276 l_int32 xpm, ypm, xp, yp, xf, yf, v00, v10, v01, v11; 01277 l_uint32 *lines; 01278 01279 PROCNAME("linearInterpolatePixelGray"); 01280 01281 if (!pval) 01282 return ERROR_INT("&val not defined", procName, 1); 01283 *pval = grayval; 01284 if (!datas) 01285 return ERROR_INT("datas not defined", procName, 1); 01286 01287 /* Skip if off the edge */ 01288 if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0) 01289 return 0; 01290 01291 xpm = (l_int32)(16.0 * x + 0.5); 01292 ypm = (l_int32)(16.0 * y + 0.5); 01293 xp = xpm >> 4; 01294 yp = ypm >> 4; 01295 xf = xpm & 0x0f; 01296 yf = ypm & 0x0f; 01297 01298 #if DEBUG 01299 if (xf < 0 || yf < 0) 01300 fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf); 01301 #endif /* DEBUG */ 01302 01303 /* Interpolate by area weighting. */ 01304 lines = datas + yp * wpls; 01305 v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp); 01306 v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1); 01307 v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp); 01308 v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1); 01309 *pval = (v00 + v01 + v10 + v11 + 128) / 256; 01310 return 0; 01311 } 01312 01313 01314 01315 /*-------------------------------------------------------------* 01316 * Gauss-jordan linear equation solver * 01317 *-------------------------------------------------------------*/ 01318 #define SWAP(a,b) {temp = (a); (a) = (b); (b) = temp;} 01319 01320 /*! 01321 * gaussjordan() 01322 * 01323 * Input: a (n x n matrix) 01324 * b (rhs column vector) 01325 * n (dimension) 01326 * Return: 0 if ok, 1 on error 01327 * 01328 * Note side effects: 01329 * (1) the matrix a is transformed to its inverse 01330 * (2) the vector b is transformed to the solution X to the 01331 * linear equation AX = B 01332 * 01333 * Adapted from "Numerical Recipes in C, Second Edition", 1992 01334 * pp. 36-41 (gauss-jordan elimination) 01335 */ 01336 l_int32 01337 gaussjordan(l_float32 **a, 01338 l_float32 *b, 01339 l_int32 n) 01340 { 01341 l_int32 i, icol, irow, j, k, l, ll; 01342 l_int32 *indexc, *indexr, *ipiv; 01343 l_float32 big, dum, pivinv, temp; 01344 01345 PROCNAME("gaussjordan"); 01346 01347 if (!a) 01348 return ERROR_INT("a not defined", procName, 1); 01349 if (!b) 01350 return ERROR_INT("b not defined", procName, 1); 01351 01352 if ((indexc = (l_int32 *)CALLOC(n, sizeof(l_int32))) == NULL) 01353 return ERROR_INT("indexc not made", procName, 1); 01354 if ((indexr = (l_int32 *)CALLOC(n, sizeof(l_int32))) == NULL) 01355 return ERROR_INT("indexr not made", procName, 1); 01356 if ((ipiv = (l_int32 *)CALLOC(n, sizeof(l_int32))) == NULL) 01357 return ERROR_INT("ipiv not made", procName, 1); 01358 01359 for (i = 0; i < n; i++) { 01360 big = 0.0; 01361 for (j = 0; j < n; j++) { 01362 if (ipiv[j] != 1) 01363 for (k = 0; k < n; k++) { 01364 if (ipiv[k] == 0) { 01365 if (fabs(a[j][k]) >= big) { 01366 big = fabs(a[j][k]); 01367 irow = j; 01368 icol = k; 01369 } 01370 } 01371 else if (ipiv[k] > 1) 01372 return ERROR_INT("singular matrix", procName, 1); 01373 } 01374 } 01375 ++(ipiv[icol]); 01376 01377 if (irow != icol) { 01378 for (l = 0; l < n; l++) 01379 SWAP(a[irow][l], a[icol][l]); 01380 SWAP(b[irow], b[icol]); 01381 } 01382 01383 indexr[i] = irow; 01384 indexc[i] = icol; 01385 if (a[icol][icol] == 0.0) 01386 return ERROR_INT("singular matrix", procName, 1); 01387 pivinv = 1.0 / a[icol][icol]; 01388 a[icol][icol] = 1.0; 01389 for (l = 0; l < n; l++) 01390 a[icol][l] *= pivinv; 01391 b[icol] *= pivinv; 01392 01393 for (ll = 0; ll < n; ll++) 01394 if (ll != icol) { 01395 dum = a[ll][icol]; 01396 a[ll][icol] = 0.0; 01397 for (l = 0; l < n; l++) 01398 a[ll][l] -= a[icol][l] * dum; 01399 b[ll] -= b[icol] * dum; 01400 } 01401 } 01402 01403 for (l = n - 1; l >= 0; l--) { 01404 if (indexr[l] != indexc[l]) 01405 for (k = 0; k < n; k++) 01406 SWAP(a[k][indexr[l]], a[k][indexc[l]]); 01407 } 01408 01409 FREE(indexr); 01410 FREE(indexc); 01411 FREE(ipiv); 01412 return 0; 01413 } 01414 01415 01416 /*-------------------------------------------------------------* 01417 * Sequential affine image transformation * 01418 *-------------------------------------------------------------*/ 01419 /*! 01420 * pixAffineSequential() 01421 * 01422 * Input: pixs 01423 * ptad (3 pts of final coordinate space) 01424 * ptas (3 pts of initial coordinate space) 01425 * bw (pixels of additional border width during computation) 01426 * bh (pixels of additional border height during computation) 01427 * Return: pixd, or null on error 01428 * 01429 * Notes: 01430 * (1) The 3 pts must not be collinear. 01431 * (2) The 3 pts must be given in this order: 01432 * - origin 01433 * - a location along the x-axis 01434 * - a location along the y-axis. 01435 * (3) You must guess how much border must be added so that no 01436 * pixels are lost in the transformations from src to 01437 * dest coordinate space. (This can be calculated but it 01438 * is a lot of work!) For coordinate spaces that are nearly 01439 * at right angles, on a 300 ppi scanned page, the addition 01440 * of 1000 pixels on each side is usually sufficient. 01441 * (4) This is here for pedagogical reasons. It is about 3x faster 01442 * on 1 bpp images than pixAffineSampled(), but the results 01443 * on text are much inferior. 01444 */ 01445 PIX * 01446 pixAffineSequential(PIX *pixs, 01447 PTA *ptad, 01448 PTA *ptas, 01449 l_int32 bw, 01450 l_int32 bh) 01451 { 01452 l_int32 x1, y1, x2, y2, x3, y3; /* ptas */ 01453 l_int32 x1p, y1p, x2p, y2p, x3p, y3p; /* ptad */ 01454 l_int32 x1sc, y1sc; /* scaled origin */ 01455 l_float32 x2s, x2sp, scalex, scaley; 01456 l_float32 th3, th3p, ph2, ph2p; 01457 l_float32 rad2deg; 01458 PIX *pixt1, *pixt2, *pixd; 01459 01460 PROCNAME("pixAffineSequential"); 01461 01462 if (!pixs) 01463 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01464 if (!ptas) 01465 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 01466 if (!ptad) 01467 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 01468 01469 if (ptaGetCount(ptas) != 3) 01470 return (PIX *)ERROR_PTR("ptas count not 3", procName, NULL); 01471 if (ptaGetCount(ptad) != 3) 01472 return (PIX *)ERROR_PTR("ptad count not 3", procName, NULL); 01473 ptaGetIPt(ptas, 0, &x1, &y1); 01474 ptaGetIPt(ptas, 1, &x2, &y2); 01475 ptaGetIPt(ptas, 2, &x3, &y3); 01476 ptaGetIPt(ptad, 0, &x1p, &y1p); 01477 ptaGetIPt(ptad, 1, &x2p, &y2p); 01478 ptaGetIPt(ptad, 2, &x3p, &y3p); 01479 01480 rad2deg = 180. / 3.1415926535; 01481 01482 if (y1 == y3) 01483 return (PIX *)ERROR_PTR("y1 == y3!", procName, NULL); 01484 if (y1p == y3p) 01485 return (PIX *)ERROR_PTR("y1p == y3p!", procName, NULL); 01486 01487 if (bw != 0 || bh != 0) { 01488 /* resize all points and add border to pixs */ 01489 x1 = x1 + bw; 01490 y1 = y1 + bh; 01491 x2 = x2 + bw; 01492 y2 = y2 + bh; 01493 x3 = x3 + bw; 01494 y3 = y3 + bh; 01495 x1p = x1p + bw; 01496 y1p = y1p + bh; 01497 x2p = x2p + bw; 01498 y2p = y2p + bh; 01499 x3p = x3p + bw; 01500 y3p = y3p + bh; 01501 01502 if ((pixt1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL) 01503 return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); 01504 } 01505 else 01506 pixt1 = pixCopy(NULL, pixs); 01507 01508 /*-------------------------------------------------------------* 01509 The horizontal shear is done to move the 3rd point to the 01510 y axis. This moves the 2nd point either towards or away 01511 from the y axis, depending on whether it is above or below 01512 the x axis. That motion must be computed so that we know 01513 the angle of vertical shear to use to get the 2nd point 01514 on the x axis. We must also know the x coordinate of the 01515 2nd point in order to compute how much scaling is required 01516 to match points on the axis. 01517 *-------------------------------------------------------------*/ 01518 01519 /* Shear angles required to put src points on x and y axes */ 01520 th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3)); 01521 x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3)); 01522 if (x2s == (l_float32)x1) 01523 return (PIX *)ERROR_PTR("x2s == x1!", procName, NULL); 01524 ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1)); 01525 01526 /* Shear angles required to put dest points on x and y axes. 01527 * Use the negative of these values to instead move the 01528 * src points from the axes to the actual dest position. 01529 * These values are also needed to scale the image. */ 01530 th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p)); 01531 x2sp = (l_float32)(x2p - ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p)); 01532 if (x2sp == (l_float32)x1p) 01533 return (PIX *)ERROR_PTR("x2sp == x1p!", procName, NULL); 01534 ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p)); 01535 01536 /* Shear image to first put src point 3 on the y axis, 01537 * and then to put src point 2 on the x axis */ 01538 pixHShearIP(pixt1, y1, th3, L_BRING_IN_WHITE); 01539 pixVShearIP(pixt1, x1, ph2, L_BRING_IN_WHITE); 01540 01541 /* Scale image to match dest scale. The dest scale 01542 * is calculated above from the angles th3p and ph2p 01543 * that would be required to move the dest points to 01544 * the x and y axes. */ 01545 scalex = (l_float32)(x2sp - x1p) / (x2s - x1); 01546 scaley = (l_float32)(y3p - y1p) / (y3 - y1); 01547 if ((pixt2 = pixScale(pixt1, scalex, scaley)) == NULL) 01548 return (PIX *)ERROR_PTR("pixt2 not made", procName, NULL); 01549 01550 #if DEBUG 01551 fprintf(stderr, "th3 = %5.1f deg, ph2 = %5.1f deg\n", 01552 rad2deg * th3, rad2deg * ph2); 01553 fprintf(stderr, "th3' = %5.1f deg, ph2' = %5.1f deg\n", 01554 rad2deg * th3p, rad2deg * ph2p); 01555 fprintf(stderr, "scalex = %6.3f, scaley = %6.3f\n", scalex, scaley); 01556 #endif /* DEBUG */ 01557 01558 /*-------------------------------------------------------------* 01559 Scaling moves the 1st src point, which is the origin. 01560 It must now be moved again to coincide with the origin 01561 (1st point) of the dest. After this is done, the 2nd 01562 and 3rd points must be sheared back to the original 01563 positions of the 2nd and 3rd dest points. We use the 01564 negative of the angles that were previously computed 01565 for shearing those points in the dest image to x and y 01566 axes, and take the shears in reverse order as well. 01567 *-------------------------------------------------------------*/ 01568 /* Shift image to match dest origin. */ 01569 x1sc = (l_int32)(scalex * x1 + 0.5); /* x comp of origin after scaling */ 01570 y1sc = (l_int32)(scaley * y1 + 0.5); /* y comp of origin after scaling */ 01571 pixRasteropIP(pixt2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE); 01572 01573 /* Shear image to take points 2 and 3 off the axis and 01574 * put them in the original dest position */ 01575 pixVShearIP(pixt2, x1p, -ph2p, L_BRING_IN_WHITE); 01576 pixHShearIP(pixt2, y1p, -th3p, L_BRING_IN_WHITE); 01577 01578 if (bw != 0 || bh != 0) { 01579 if ((pixd = pixRemoveBorderGeneral(pixt2, bw, bw, bh, bh)) == NULL) 01580 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 01581 } 01582 else 01583 pixd = pixClone(pixt2); 01584 01585 pixDestroy(&pixt1); 01586 pixDestroy(&pixt2); 01587 return pixd; 01588 } 01589 01590