Leptonica 1.68
C Image Processing Library

projective.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines