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 * projective.c 00018 * 00019 * Projective (4 pt) image transformation using a sampled 00020 * (to nearest integer) transform on each dest point 00021 * PIX *pixProjectiveSampledPta() 00022 * PIX *pixProjectiveSampled() 00023 * 00024 * Projective (4 pt) image transformation using interpolation 00025 * (or area mapping) for anti-aliasing images that are 00026 * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB 00027 * PIX *pixProjectivePta() 00028 * PIX *pixProjective() 00029 * PIX *pixProjectivePtaColor() 00030 * PIX *pixProjectiveColor() 00031 * PIX *pixProjectivePtaGray() 00032 * PIX *pixProjectiveGray() 00033 * 00034 * Projective transform including alpha (blend) component and gamma xform 00035 * PIX *pixProjectivePtaWithAlpha() 00036 * PIX *pixProjectivePtaGammaXform() 00037 * 00038 * Projective coordinate transformation 00039 * l_int32 getProjectiveXformCoeffs() 00040 * l_int32 projectiveXformSampledPt() 00041 * l_int32 projectiveXformPt() 00042 * 00043 * A projective transform can be specified as a specific functional 00044 * mapping between 4 points in the source and 4 points in the dest. 00045 * It preserves straight lines, but is less stable than a bilinear 00046 * transform, because it contains a division by a quantity that 00047 * can get arbitrarily small.) 00048 * 00049 * We give both a projective coordinate transformation and 00050 * two projective image transformations. 00051 * 00052 * For the former, we ask for the coordinate value (x',y') 00053 * in the transformed space for any point (x,y) in the original 00054 * space. The coefficients of the transformation are found by 00055 * solving 8 simultaneous equations for the 8 coordinates of 00056 * the 4 points in src and dest. The transformation can then 00057 * be used to compute the associated image transform, by 00058 * computing, for each dest pixel, the relevant pixel(s) in 00059 * the source. This can be done either by taking the closest 00060 * src pixel to each transformed dest pixel ("sampling") or 00061 * by doing an interpolation and averaging over 4 source 00062 * pixels with appropriate weightings ("interpolated"). 00063 * 00064 * A typical application would be to remove keystoning 00065 * due to a projective transform in the imaging system. 00066 * 00067 * The projective transform is given by specifying two equations: 00068 * 00069 * x' = (ax + by + c) / (gx + hy + 1) 00070 * y' = (dx + ey + f) / (gx + hy + 1) 00071 * 00072 * where the eight coefficients have been computed from four 00073 * sets of these equations, each for two corresponding data pts. 00074 * In practice, for each point (x,y) in the dest image, this 00075 * equation is used to compute the corresponding point (x',y') 00076 * in the src. That computed point in the src is then used 00077 * to determine the dest value in one of two ways: 00078 * 00079 * - sampling: take the value of the src pixel in which this 00080 * point falls 00081 * - interpolation: take appropriate linear combinations of the 00082 * four src pixels that this dest pixel would 00083 * overlap, with the coefficients proportional 00084 * to the amount of overlap 00085 * 00086 * For small warp where there is little scale change, (e.g., 00087 * for rotation) area mapping is nearly equivalent to interpolation. 00088 * 00089 * Typical relative timing of pointwise transforms (sampled = 1.0): 00090 * 8 bpp: sampled 1.0 00091 * interpolated 1.5 00092 * 32 bpp: sampled 1.0 00093 * interpolated 1.6 00094 * Additionally, the computation time/pixel is nearly the same 00095 * for 8 bpp and 32 bpp, for both sampled and interpolated. 00096 */ 00097 00098 #include <stdio.h> 00099 #include <stdlib.h> 00100 #include <string.h> 00101 #include <math.h> 00102 #include "allheaders.h" 00103 00104 extern l_float32 AlphaMaskBorderVals[2]; 00105 00106 00107 /*------------------------------------------------------------n 00108 * Sampled projective image transformation * 00109 *-------------------------------------------------------------*/ 00110 /*! 00111 * pixProjectiveSampledPta() 00112 * 00113 * Input: pixs (all depths) 00114 * ptad (4 pts of final coordinate space) 00115 * ptas (4 pts of initial coordinate space) 00116 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00117 * Return: pixd, or null on error 00118 * 00119 * Notes: 00120 * (1) Brings in either black or white pixels from the boundary. 00121 * (2) Retains colormap, which you can do for a sampled transform.. 00122 * (3) No 3 of the 4 points may be collinear. 00123 * (4) For 8 and 32 bpp pix, better quality is obtained by the 00124 * somewhat slower pixProjectivePta(). See that 00125 * function for relative timings between sampled and interpolated. 00126 */ 00127 PIX * 00128 pixProjectiveSampledPta(PIX *pixs, 00129 PTA *ptad, 00130 PTA *ptas, 00131 l_int32 incolor) 00132 { 00133 l_float32 *vc; 00134 PIX *pixd; 00135 00136 PROCNAME("pixProjectiveSampledPta"); 00137 00138 if (!pixs) 00139 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00140 if (!ptas) 00141 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00142 if (!ptad) 00143 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00144 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00145 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00146 if (ptaGetCount(ptas) != 4) 00147 return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); 00148 if (ptaGetCount(ptad) != 4) 00149 return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); 00150 00151 /* Get backwards transform from dest to src, and apply it */ 00152 getProjectiveXformCoeffs(ptad, ptas, &vc); 00153 pixd = pixProjectiveSampled(pixs, vc, incolor); 00154 FREE(vc); 00155 00156 return pixd; 00157 } 00158 00159 00160 /*! 00161 * pixProjectiveSampled() 00162 * 00163 * Input: pixs (all depths) 00164 * vc (vector of 8 coefficients for projective transformation) 00165 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00166 * Return: pixd, or null on error 00167 * 00168 * Notes: 00169 * (1) Brings in either black or white pixels from the boundary. 00170 * (2) Retains colormap, which you can do for a sampled transform.. 00171 * (3) For 8 or 32 bpp, much better quality is obtained by the 00172 * somewhat slower pixProjective(). See that function 00173 * for relative timings between sampled and interpolated. 00174 */ 00175 PIX * 00176 pixProjectiveSampled(PIX *pixs, 00177 l_float32 *vc, 00178 l_int32 incolor) 00179 { 00180 l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex; 00181 l_uint32 val; 00182 l_uint32 *datas, *datad, *lines, *lined; 00183 PIX *pixd; 00184 PIXCMAP *cmap; 00185 00186 PROCNAME("pixProjectiveSampled"); 00187 00188 if (!pixs) 00189 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00190 if (!vc) 00191 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00192 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00193 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00194 pixGetDimensions(pixs, &w, &h, &d); 00195 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32) 00196 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", procName, NULL); 00197 00198 /* Init all dest pixels to color to be brought in from outside */ 00199 pixd = pixCreateTemplate(pixs); 00200 if ((cmap = pixGetColormap(pixs)) != NULL) { 00201 if (incolor == L_BRING_IN_WHITE) 00202 color = 1; 00203 else 00204 color = 0; 00205 pixcmapAddBlackOrWhite(cmap, color, &cmapindex); 00206 pixSetAllArbitrary(pixd, cmapindex); 00207 } 00208 else { 00209 if ((d == 1 && incolor == L_BRING_IN_WHITE) || 00210 (d > 1 && incolor == L_BRING_IN_BLACK)) 00211 pixClearAll(pixd); 00212 else 00213 pixSetAll(pixd); 00214 } 00215 00216 /* Scan over the dest pixels */ 00217 datas = pixGetData(pixs); 00218 wpls = pixGetWpl(pixs); 00219 datad = pixGetData(pixd); 00220 wpld = pixGetWpl(pixd); 00221 for (i = 0; i < h; i++) { 00222 lined = datad + i * wpld; 00223 for (j = 0; j < w; j++) { 00224 projectiveXformSampledPt(vc, j, i, &x, &y); 00225 if (x < 0 || y < 0 || x >=w || y >= h) 00226 continue; 00227 lines = datas + y * wpls; 00228 if (d == 1) { 00229 val = GET_DATA_BIT(lines, x); 00230 SET_DATA_BIT_VAL(lined, j, val); 00231 } 00232 else if (d == 8) { 00233 val = GET_DATA_BYTE(lines, x); 00234 SET_DATA_BYTE(lined, j, val); 00235 } 00236 else if (d == 32) { 00237 lined[j] = lines[x]; 00238 } 00239 else if (d == 2) { 00240 val = GET_DATA_DIBIT(lines, x); 00241 SET_DATA_DIBIT(lined, j, val); 00242 } 00243 else if (d == 4) { 00244 val = GET_DATA_QBIT(lines, x); 00245 SET_DATA_QBIT(lined, j, val); 00246 } 00247 } 00248 } 00249 00250 return pixd; 00251 } 00252 00253 00254 /*---------------------------------------------------------------------* 00255 * Interpolated projective image transformation * 00256 *---------------------------------------------------------------------*/ 00257 /*! 00258 * pixProjectivePta() 00259 * 00260 * Input: pixs (all depths; colormap ok) 00261 * ptad (4 pts of final coordinate space) 00262 * ptas (4 pts of initial coordinate space) 00263 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00264 * Return: pixd, or null on error 00265 * 00266 * Notes: 00267 * (1) Brings in either black or white pixels from the boundary 00268 * (2) Removes any existing colormap, if necessary, before transforming 00269 */ 00270 PIX * 00271 pixProjectivePta(PIX *pixs, 00272 PTA *ptad, 00273 PTA *ptas, 00274 l_int32 incolor) 00275 { 00276 l_int32 d; 00277 l_uint32 colorval; 00278 PIX *pixt1, *pixt2, *pixd; 00279 00280 PROCNAME("pixProjectivePta"); 00281 00282 if (!pixs) 00283 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00284 if (!ptas) 00285 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00286 if (!ptad) 00287 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00288 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00289 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00290 if (ptaGetCount(ptas) != 4) 00291 return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); 00292 if (ptaGetCount(ptad) != 4) 00293 return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); 00294 00295 if (pixGetDepth(pixs) == 1) 00296 return pixProjectiveSampledPta(pixs, ptad, ptas, incolor); 00297 00298 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ 00299 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00300 d = pixGetDepth(pixt1); 00301 if (d < 8) 00302 pixt2 = pixConvertTo8(pixt1, FALSE); 00303 else 00304 pixt2 = pixClone(pixt1); 00305 d = pixGetDepth(pixt2); 00306 00307 /* Compute actual color to bring in from edges */ 00308 colorval = 0; 00309 if (incolor == L_BRING_IN_WHITE) { 00310 if (d == 8) 00311 colorval = 255; 00312 else /* d == 32 */ 00313 colorval = 0xffffff00; 00314 } 00315 00316 if (d == 8) 00317 pixd = pixProjectivePtaGray(pixt2, ptad, ptas, colorval); 00318 else /* d == 32 */ 00319 pixd = pixProjectivePtaColor(pixt2, ptad, ptas, colorval); 00320 pixDestroy(&pixt1); 00321 pixDestroy(&pixt2); 00322 return pixd; 00323 } 00324 00325 00326 /*! 00327 * pixProjective() 00328 * 00329 * Input: pixs (all depths; colormap ok) 00330 * vc (vector of 8 coefficients for projective transformation) 00331 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00332 * Return: pixd, or null on error 00333 * 00334 * Notes: 00335 * (1) Brings in either black or white pixels from the boundary 00336 * (2) Removes any existing colormap, if necessary, before transforming 00337 */ 00338 PIX * 00339 pixProjective(PIX *pixs, 00340 l_float32 *vc, 00341 l_int32 incolor) 00342 { 00343 l_int32 d; 00344 l_uint32 colorval; 00345 PIX *pixt1, *pixt2, *pixd; 00346 00347 PROCNAME("pixProjective"); 00348 00349 if (!pixs) 00350 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00351 if (!vc) 00352 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00353 00354 if (pixGetDepth(pixs) == 1) 00355 return pixProjectiveSampled(pixs, vc, incolor); 00356 00357 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ 00358 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00359 d = pixGetDepth(pixt1); 00360 if (d < 8) 00361 pixt2 = pixConvertTo8(pixt1, FALSE); 00362 else 00363 pixt2 = pixClone(pixt1); 00364 d = pixGetDepth(pixt2); 00365 00366 /* Compute actual color to bring in from edges */ 00367 colorval = 0; 00368 if (incolor == L_BRING_IN_WHITE) { 00369 if (d == 8) 00370 colorval = 255; 00371 else /* d == 32 */ 00372 colorval = 0xffffff00; 00373 } 00374 00375 if (d == 8) 00376 pixd = pixProjectiveGray(pixt2, vc, colorval); 00377 else /* d == 32 */ 00378 pixd = pixProjectiveColor(pixt2, vc, colorval); 00379 pixDestroy(&pixt1); 00380 pixDestroy(&pixt2); 00381 return pixd; 00382 } 00383 00384 00385 /*! 00386 * pixProjectivePtaColor() 00387 * 00388 * Input: pixs (32 bpp) 00389 * ptad (4 pts of final coordinate space) 00390 * ptas (4 pts of initial coordinate space) 00391 * colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE) 00392 * Return: pixd, or null on error 00393 */ 00394 PIX * 00395 pixProjectivePtaColor(PIX *pixs, 00396 PTA *ptad, 00397 PTA *ptas, 00398 l_uint32 colorval) 00399 { 00400 l_float32 *vc; 00401 PIX *pixd; 00402 00403 PROCNAME("pixProjectivePtaColor"); 00404 00405 if (!pixs) 00406 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00407 if (!ptas) 00408 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00409 if (!ptad) 00410 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00411 if (pixGetDepth(pixs) != 32) 00412 return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); 00413 if (ptaGetCount(ptas) != 4) 00414 return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); 00415 if (ptaGetCount(ptad) != 4) 00416 return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); 00417 00418 /* Get backwards transform from dest to src, and apply it */ 00419 getProjectiveXformCoeffs(ptad, ptas, &vc); 00420 pixd = pixProjectiveColor(pixs, vc, colorval); 00421 FREE(vc); 00422 00423 return pixd; 00424 } 00425 00426 00427 /*! 00428 * pixProjectiveColor() 00429 * 00430 * Input: pixs (32 bpp) 00431 * vc (vector of 8 coefficients for projective transformation) 00432 * colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE) 00433 * Return: pixd, or null on error 00434 */ 00435 PIX * 00436 pixProjectiveColor(PIX *pixs, 00437 l_float32 *vc, 00438 l_uint32 colorval) 00439 { 00440 l_int32 i, j, w, h, d, wpls, wpld; 00441 l_uint32 val; 00442 l_uint32 *datas, *datad, *lined; 00443 l_float32 x, y; 00444 PIX *pixd; 00445 00446 PROCNAME("pixProjectiveColor"); 00447 00448 if (!pixs) 00449 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00450 pixGetDimensions(pixs, &w, &h, &d); 00451 if (d != 32) 00452 return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); 00453 if (!vc) 00454 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00455 00456 datas = pixGetData(pixs); 00457 wpls = pixGetWpl(pixs); 00458 pixd = pixCreateTemplate(pixs); 00459 pixSetAllArbitrary(pixd, colorval); 00460 datad = pixGetData(pixd); 00461 wpld = pixGetWpl(pixd); 00462 00463 /* Iterate over destination pixels */ 00464 for (i = 0; i < h; i++) { 00465 lined = datad + i * wpld; 00466 for (j = 0; j < w; j++) { 00467 /* Compute float src pixel location corresponding to (i,j) */ 00468 projectiveXformPt(vc, j, i, &x, &y); 00469 linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval, 00470 &val); 00471 *(lined + j) = val; 00472 } 00473 } 00474 00475 return pixd; 00476 } 00477 00478 00479 /*! 00480 * pixProjectivePtaGray() 00481 * 00482 * Input: pixs (8 bpp) 00483 * ptad (4 pts of final coordinate space) 00484 * ptas (4 pts of initial coordinate space) 00485 * grayval (0 to bring in BLACK, 255 for WHITE) 00486 * Return: pixd, or null on error 00487 */ 00488 PIX * 00489 pixProjectivePtaGray(PIX *pixs, 00490 PTA *ptad, 00491 PTA *ptas, 00492 l_uint8 grayval) 00493 { 00494 l_float32 *vc; 00495 PIX *pixd; 00496 00497 PROCNAME("pixProjectivePtaGray"); 00498 00499 if (!pixs) 00500 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00501 if (!ptas) 00502 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00503 if (!ptad) 00504 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00505 if (pixGetDepth(pixs) != 8) 00506 return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); 00507 if (ptaGetCount(ptas) != 4) 00508 return (PIX *)ERROR_PTR("ptas count not 4", procName, NULL); 00509 if (ptaGetCount(ptad) != 4) 00510 return (PIX *)ERROR_PTR("ptad count not 4", procName, NULL); 00511 00512 /* Get backwards transform from dest to src, and apply it */ 00513 getProjectiveXformCoeffs(ptad, ptas, &vc); 00514 pixd = pixProjectiveGray(pixs, vc, grayval); 00515 FREE(vc); 00516 00517 return pixd; 00518 } 00519 00520 00521 00522 /*! 00523 * pixProjectiveGray() 00524 * 00525 * Input: pixs (8 bpp) 00526 * vc (vector of 8 coefficients for projective transformation) 00527 * grayval (0 to bring in BLACK, 255 for WHITE) 00528 * Return: pixd, or null on error 00529 */ 00530 PIX * 00531 pixProjectiveGray(PIX *pixs, 00532 l_float32 *vc, 00533 l_uint8 grayval) 00534 { 00535 l_int32 i, j, w, h, wpls, wpld, val; 00536 l_uint32 *datas, *datad, *lined; 00537 l_float32 x, y; 00538 PIX *pixd; 00539 00540 PROCNAME("pixProjectiveGray"); 00541 00542 if (!pixs) 00543 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00544 pixGetDimensions(pixs, &w, &h, NULL); 00545 if (pixGetDepth(pixs) != 8) 00546 return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL); 00547 if (!vc) 00548 return (PIX *)ERROR_PTR("vc not defined", procName, NULL); 00549 00550 datas = pixGetData(pixs); 00551 wpls = pixGetWpl(pixs); 00552 pixd = pixCreateTemplate(pixs); 00553 pixSetAllArbitrary(pixd, grayval); 00554 datad = pixGetData(pixd); 00555 wpld = pixGetWpl(pixd); 00556 00557 /* Iterate over destination pixels */ 00558 for (i = 0; i < h; i++) { 00559 lined = datad + i * wpld; 00560 for (j = 0; j < w; j++) { 00561 /* Compute float src pixel location corresponding to (i,j) */ 00562 projectiveXformPt(vc, j, i, &x, &y); 00563 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); 00564 SET_DATA_BYTE(lined, j, val); 00565 } 00566 } 00567 00568 return pixd; 00569 } 00570 00571 00572 /*---------------------------------------------------------------------------* 00573 * Projective transform including alpha (blend) component and gamma xform * 00574 *---------------------------------------------------------------------------*/ 00575 /*! 00576 * pixProjectivePtaWithAlpha() 00577 * 00578 * Input: pixs (32 bpp rgb) 00579 * ptad (4 pts of final coordinate space) 00580 * ptas (4 pts of initial coordinate space) 00581 * pixg (<optional> 8 bpp, for alpha channel, can be null) 00582 * fract (between 0.0 and 1.0, with 0.0 fully transparent 00583 * and 1.0 fully opaque) 00584 * border (of pixels added to capture transformed source pixels) 00585 * Return: pixd, or null on error 00586 * 00587 * Notes: 00588 * (1) The alpha channel is transformed separately from pixs, 00589 * and aligns with it, being fully transparent outside the 00590 * boundary of the transformed pixs. For pixels that are fully 00591 * transparent, a blending function like pixBlendWithGrayMask() 00592 * will give zero weight to corresponding pixels in pixs. 00593 * (2) If pixg is NULL, it is generated as an alpha layer that is 00594 * partially opaque, using @fract. Otherwise, it is cropped 00595 * to pixs if required and @fract is ignored. The alpha channel 00596 * in pixs is never used. 00597 * (3) Colormaps are removed. 00598 * (4) When pixs is transformed, it doesn't matter what color is brought 00599 * in because the alpha channel will be transparent (0) there. 00600 * (5) To avoid losing source pixels in the destination, it may be 00601 * necessary to add a border to the source pix before doing 00602 * the projective transformation. This can be any non-negative 00603 * number. 00604 * (6) The input @ptad and @ptas are in a coordinate space before 00605 * the border is added. Internally, we compensate for this 00606 * before doing the projective transform on the image after 00607 * the border is added. 00608 * (7) The default setting for the border values in the alpha channel 00609 * is 0 (transparent) for the outermost ring of pixels and 00610 * (0.5 * fract * 255) for the second ring. When blended over 00611 * a second image, this 00612 * (a) shrinks the visible image to make a clean overlap edge 00613 * with an image below, and 00614 * (b) softens the edges by weakening the aliasing there. 00615 * Use l_setAlphaMaskBorder() to change these values. 00616 */ 00617 PIX * 00618 pixProjectivePtaWithAlpha(PIX *pixs, 00619 PTA *ptad, 00620 PTA *ptas, 00621 PIX *pixg, 00622 l_float32 fract, 00623 l_int32 border) 00624 { 00625 l_int32 ws, hs, d; 00626 PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; 00627 PTA *ptad2, *ptas2; 00628 00629 PROCNAME("pixProjectivePtaWithAlpha"); 00630 00631 if (!pixs) 00632 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00633 pixGetDimensions(pixs, &ws, &hs, &d); 00634 if (d != 32 && pixGetColormap(pixs) == NULL) 00635 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 00636 if (pixg && pixGetDepth(pixg) != 8) { 00637 L_WARNING("pixg not 8 bpp; using @fract transparent alpha", procName); 00638 pixg = NULL; 00639 } 00640 if (!pixg && (fract < 0.0 || fract > 1.0)) { 00641 L_WARNING("invalid fract; using 1.0 (fully transparent)", procName); 00642 fract = 1.0; 00643 } 00644 if (!pixg && fract == 0.0) 00645 L_WARNING("fully opaque alpha; image will not be blended", procName); 00646 if (!ptad) 00647 return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); 00648 if (!ptas) 00649 return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); 00650 00651 /* Add border; the color doesn't matter */ 00652 pixb1 = pixAddBorder(pixs, border, 0); 00653 00654 /* Transform the ptr arrays to work on the bordered image */ 00655 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); 00656 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); 00657 00658 /* Do separate projective transform of rgb channels of pixs 00659 * and of pixg */ 00660 pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); 00661 if (!pixg) { 00662 pixg2 = pixCreate(ws, hs, 8); 00663 if (fract == 1.0) 00664 pixSetAll(pixg2); 00665 else 00666 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); 00667 } 00668 else 00669 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); 00670 if (ws > 10 && hs > 10) { /* see note 7 */ 00671 pixSetBorderRingVal(pixg2, 1, 00672 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); 00673 pixSetBorderRingVal(pixg2, 2, 00674 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); 00675 00676 } 00677 pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ 00678 pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); 00679 pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); 00680 00681 pixDestroy(&pixg2); 00682 pixDestroy(&pixb1); 00683 pixDestroy(&pixb2); 00684 pixDestroy(&pixga); 00685 ptaDestroy(&ptad2); 00686 ptaDestroy(&ptas2); 00687 return pixd; 00688 } 00689 00690 00691 /*! 00692 * pixProjectivePtaGammaXform() 00693 * 00694 * Input: pixs (32 bpp rgb) 00695 * gamma (gamma correction; must be > 0.0) 00696 * ptad (3 pts of final coordinate space) 00697 * ptas (3 pts of initial coordinate space) 00698 * fract (between 0.0 and 1.0, with 1.0 fully transparent) 00699 * border (of pixels to capture transformed source pixels) 00700 * Return: pixd, or null on error 00701 * 00702 * Notes: 00703 * (1) This wraps a gamma/inverse-gamma photometric transform around 00704 * pixProjectivePtaWithAlpha(). 00705 * (2) For usage, see notes in pixProjectivePtaWithAlpha() and 00706 * pixGammaTRCWithAlpha(). 00707 * (3) The basic idea of a gamma/inverse-gamma transform is to remove 00708 * any gamma correction before the projective transform, and restore 00709 * it afterward. The effects can be subtle, but important for 00710 * some applications. For example, using gamma > 1.0 will 00711 * cause the dark areas to become somewhat lighter and slightly 00712 * reduce aliasing effects when blending using the alpha channel. 00713 */ 00714 PIX * 00715 pixProjectivePtaGammaXform(PIX *pixs, 00716 l_float32 gamma, 00717 PTA *ptad, 00718 PTA *ptas, 00719 l_float32 fract, 00720 l_int32 border) 00721 { 00722 PIX *pixg, *pixd; 00723 00724 PROCNAME("pixProjectivePtaGammaXform"); 00725 00726 if (!pixs || (pixGetDepth(pixs) != 32)) 00727 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); 00728 if (fract == 0.0) 00729 L_WARNING("fully opaque alpha; image cannot be blended", procName); 00730 if (gamma <= 0.0) { 00731 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00732 gamma = 1.0; 00733 } 00734 00735 pixg = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255); 00736 pixd = pixProjectivePtaWithAlpha(pixg, ptad, ptas, NULL, fract, border); 00737 pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255); 00738 pixDestroy(&pixg); 00739 return pixd; 00740 } 00741 00742 00743 00744 /*-------------------------------------------------------------* 00745 * Projective coordinate transformation * 00746 *-------------------------------------------------------------*/ 00747 /*! 00748 * getProjectiveXformCoeffs() 00749 * 00750 * Input: ptas (source 4 points; unprimed) 00751 * ptad (transformed 4 points; primed) 00752 * &vc (<return> vector of coefficients of transform) 00753 * Return: 0 if OK; 1 on error 00754 * 00755 * We have a set of 8 equations, describing the projective 00756 * transformation that takes 4 points (ptas) into 4 other 00757 * points (ptad). These equations are: 00758 * 00759 * x1' = (c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1) 00760 * y1' = (c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1) 00761 * x2' = (c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1) 00762 * y2' = (c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1) 00763 * x3' = (c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1) 00764 * y3' = (c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1) 00765 * x4' = (c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1) 00766 * y4' = (c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1) 00767 * 00768 * Multiplying both sides of each eqn by the denominator, we get 00769 * 00770 * AC = B 00771 * 00772 * where B and C are column vectors 00773 * 00774 * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ] 00775 * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ] 00776 * 00777 * and A is the 8x8 matrix 00778 * 00779 * x1 y1 1 0 0 0 -x1*x1' -y1*x1' 00780 * 0 0 0 x1 y1 1 -x1*y1' -y1*y1' 00781 * x2 y2 1 0 0 0 -x2*x2' -y2*x2' 00782 * 0 0 0 x2 y2 1 -x2*y2' -y2*y2' 00783 * x3 y3 1 0 0 0 -x3*x3' -y3*x3' 00784 * 0 0 0 x3 y3 1 -x3*y3' -y3*y3' 00785 * x4 y4 1 0 0 0 -x4*x4' -y4*x4' 00786 * 0 0 0 x4 y4 1 -x4*y4' -y4*y4' 00787 * 00788 * These eight equations are solved here for the coefficients C. 00789 * 00790 * These eight coefficients can then be used to find the mapping 00791 * (x,y) --> (x',y'): 00792 * 00793 * x' = (c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1) 00794 * y' = (c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1) 00795 * 00796 * that is implemented in projectiveXformSampled() and 00797 * projectiveXFormInterpolated(). 00798 */ 00799 l_int32 00800 getProjectiveXformCoeffs(PTA *ptas, 00801 PTA *ptad, 00802 l_float32 **pvc) 00803 { 00804 l_int32 i; 00805 l_float32 x1, y1, x2, y2, x3, y3, x4, y4; 00806 l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */ 00807 l_float32 *a[8]; /* 8x8 matrix A */ 00808 00809 PROCNAME("getProjectiveXformCoeffs"); 00810 00811 if (!ptas) 00812 return ERROR_INT("ptas not defined", procName, 1); 00813 if (!ptad) 00814 return ERROR_INT("ptad not defined", procName, 1); 00815 if (!pvc) 00816 return ERROR_INT("&vc not defined", procName, 1); 00817 00818 if ((b = (l_float32 *)CALLOC(8, sizeof(l_float32))) == NULL) 00819 return ERROR_INT("b not made", procName, 1); 00820 *pvc = b; 00821 00822 ptaGetPt(ptas, 0, &x1, &y1); 00823 ptaGetPt(ptas, 1, &x2, &y2); 00824 ptaGetPt(ptas, 2, &x3, &y3); 00825 ptaGetPt(ptas, 3, &x4, &y4); 00826 ptaGetPt(ptad, 0, &b[0], &b[1]); 00827 ptaGetPt(ptad, 1, &b[2], &b[3]); 00828 ptaGetPt(ptad, 2, &b[4], &b[5]); 00829 ptaGetPt(ptad, 3, &b[6], &b[7]); 00830 00831 for (i = 0; i < 8; i++) { 00832 if ((a[i] = (l_float32 *)CALLOC(8, sizeof(l_float32))) == NULL) 00833 return ERROR_INT("a[i] not made", procName, 1); 00834 } 00835 00836 a[0][0] = x1; 00837 a[0][1] = y1; 00838 a[0][2] = 1.; 00839 a[0][6] = -x1 * b[0]; 00840 a[0][7] = -y1 * b[0]; 00841 a[1][3] = x1; 00842 a[1][4] = y1; 00843 a[1][5] = 1; 00844 a[1][6] = -x1 * b[1]; 00845 a[1][7] = -y1 * b[1]; 00846 a[2][0] = x2; 00847 a[2][1] = y2; 00848 a[2][2] = 1.; 00849 a[2][6] = -x2 * b[2]; 00850 a[2][7] = -y2 * b[2]; 00851 a[3][3] = x2; 00852 a[3][4] = y2; 00853 a[3][5] = 1; 00854 a[3][6] = -x2 * b[3]; 00855 a[3][7] = -y2 * b[3]; 00856 a[4][0] = x3; 00857 a[4][1] = y3; 00858 a[4][2] = 1.; 00859 a[4][6] = -x3 * b[4]; 00860 a[4][7] = -y3 * b[4]; 00861 a[5][3] = x3; 00862 a[5][4] = y3; 00863 a[5][5] = 1; 00864 a[5][6] = -x3 * b[5]; 00865 a[5][7] = -y3 * b[5]; 00866 a[6][0] = x4; 00867 a[6][1] = y4; 00868 a[6][2] = 1.; 00869 a[6][6] = -x4 * b[6]; 00870 a[6][7] = -y4 * b[6]; 00871 a[7][3] = x4; 00872 a[7][4] = y4; 00873 a[7][5] = 1; 00874 a[7][6] = -x4 * b[7]; 00875 a[7][7] = -y4 * b[7]; 00876 00877 gaussjordan(a, b, 8); 00878 00879 for (i = 0; i < 8; i++) 00880 FREE(a[i]); 00881 00882 return 0; 00883 } 00884 00885 00886 /*! 00887 * projectiveXformSampledPt() 00888 * 00889 * Input: vc (vector of 8 coefficients) 00890 * (x, y) (initial point) 00891 * (&xp, &yp) (<return> transformed point) 00892 * Return: 0 if OK; 1 on error 00893 * 00894 * Notes: 00895 * (1) This finds the nearest pixel coordinates of the transformed point. 00896 * (2) It does not check ptrs for returned data! 00897 */ 00898 l_int32 00899 projectiveXformSampledPt(l_float32 *vc, 00900 l_int32 x, 00901 l_int32 y, 00902 l_int32 *pxp, 00903 l_int32 *pyp) 00904 { 00905 l_float32 factor; 00906 00907 PROCNAME("projectiveXformSampledPt"); 00908 00909 if (!vc) 00910 return ERROR_INT("vc not defined", procName, 1); 00911 00912 factor = 1. / (vc[6] * x + vc[7] * y + 1.); 00913 *pxp = (l_int32)(factor * (vc[0] * x + vc[1] * y + vc[2]) + 0.5); 00914 *pyp = (l_int32)(factor * (vc[3] * x + vc[4] * y + vc[5]) + 0.5); 00915 return 0; 00916 } 00917 00918 00919 /*! 00920 * projectiveXformPt() 00921 * 00922 * Input: vc (vector of 8 coefficients) 00923 * (x, y) (initial point) 00924 * (&xp, &yp) (<return> transformed point) 00925 * Return: 0 if OK; 1 on error 00926 * 00927 * Notes: 00928 * (1) This computes the floating point location of the transformed point. 00929 * (2) It does not check ptrs for returned data! 00930 */ 00931 l_int32 00932 projectiveXformPt(l_float32 *vc, 00933 l_int32 x, 00934 l_int32 y, 00935 l_float32 *pxp, 00936 l_float32 *pyp) 00937 { 00938 l_float32 factor; 00939 00940 PROCNAME("projectiveXformPt"); 00941 00942 if (!vc) 00943 return ERROR_INT("vc not defined", procName, 1); 00944 00945 factor = 1. / (vc[6] * x + vc[7] * y + 1.); 00946 *pxp = factor * (vc[0] * x + vc[1] * y + vc[2]); 00947 *pyp = factor * (vc[3] * x + vc[4] * y + vc[5]); 00948 return 0; 00949 } 00950 00951