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