Changeset 11612 for ImageMagick/trunk

Show
Ignore:
Timestamp:
07/12/08 01:01:08 (6 weeks ago)
Author:
anthony
Message:

Separate coefficient calculation from DistortImages?() (future development)

Perspective and Bilinear Distortions fallback to Affine
when less than 4 point pairs are provided.

Affine Distortion now handles 2 point pairs (SRT of a single line)
and a simple translation if only a single point pair is provided.

Location:
ImageMagick/trunk
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • ImageMagick/trunk/ChangeLog

    r11592 r11612  
     12008-07-12  6.4.2-2 Anthony <anthony@griffith...> 
     2  * Separate coefficient calculation from DistortImages (future development) 
     3  * Perspective and Bilinear Distortions fallback to Affine 
     4    when less than 4 point pairs are provided. 
     5  * Affine Distortion now handles 2 point pairs (SRT of a single line) 
     6    and a simple translation if only a single point pair is provided. 
     7 
    182008-06-29  6.4.2-2 Cristy  <quetzlzacatenango@image...> 
    29  * Add log2 and round as -fx operators. 
  • ImageMagick/trunk/magick/distort.c

    r11610 r11612  
    8686%  account in the mapping. 
    8787% 
     88% 
     89%  The format of the DistortImage() method is: 
     90% 
     91%      Image *DistortImage(const Image *image,const DistortImageMethod method, 
     92%        const unsigned long number_arguments,const double *arguments, 
     93%        MagickBooleanType bestfit, ExceptionInfo *exception) 
     94% 
     95%  A description of each parameter follows: 
     96% 
     97%    o image: the image to be distorted. 
     98% 
     99%    o method: the method of image distortion. 
     100% 
     101%    o number_arguments: the number of arguments given. 
     102% 
     103%    o arguments: the arguments for this distortion method. 
     104% 
     105%    o bestfit: Attempt to 'bestfit' the size of the resulting image. 
     106%         This also forces the resulting image to be a 'layered' virtual 
     107%         canvas image.  Can be overridden using 'distort:viewport' setting. 
     108% 
     109%    o exception: Return any errors or warnings in this structure 
     110% 
     111% Specific Distortion notes... 
     112% 
    88113%  ArcDistortion will always ignore source image offset, and always 'bestfit' 
    89114%  the destination image with the top left corner offset relative to the polar 
     
    93118%  of image distortion. 
    94119% 
    95 %  The format of the DistortImage() method is: 
    96 % 
    97 %      Image *DistortImage(const Image *image,const DistortImageMethod method, 
    98 %        const unsigned long number_arguments,const double *arguments, 
    99 %        MagickBooleanType bestfit, ExceptionInfo *exception) 
    100 % 
    101 %  A description of each parameter follows: 
    102 % 
    103 %    o image: the image to be distorted. 
    104 % 
    105 %    o method: the method of image distortion. 
    106 % 
    107 %    o number_arguments: the number of arguments given. 
    108 % 
    109 %    o arguments: the arguments for this distortion method. 
    110 % 
    111 %    o bestfit: Attempt to resize destination to fit distorted source. 
    112 % 
    113 %    o exception: Return any errors or warnings in this structure 
     120%  Affine, Perspective, and Bilinear, will do least squares fitting of the 
     121%  distrotion when more than the minimum number of control point pairs are 
     122%  provided. 
     123% 
     124%  Perspective, and Bilinear, will fall back to a Affine distortion when less 
     125%  that 4 control point pairs are provided. While Affine distortions will let 
     126%  you use any number of control point pairs, that is Zero pairs is a No-Op 
     127%  (viewport only) distrotion, one pair is a translation and two pairs of 
     128%  control points will do a scale-rotate-translate, without any shearing. 
    114129% 
    115130*/ 
     
    192207 
    193208 
    194 MagickExport Image *DistortImage(Image *image,const DistortImageMethod method, 
    195   const unsigned long number_arguments,const double *arguments, 
    196   MagickBooleanType bestfit,ExceptionInfo *exception) 
     209static double *DistortCoefficents(Image *image, DistortImageMethod *method, 
     210     const unsigned long number_arguments, const double *arguments, 
     211     MagickBooleanType bestfit, ExceptionInfo *exception) 
    197212{ 
    198 #define DistortImageTag  "Distort/Image" 
    199  
    200   const char 
    201     *artifact; 
     213  /* Determine correct coefficients for the given input arguments. 
     214     The given distortion method may be modified, to use a simplier 
     215     distortion method to better handle the requested arguments. 
     216 
     217     The returned array of coefficients, will need to be freed by the caller. 
     218     by calling    RelinquishMagickMemory() 
     219 
     220     If NULL is returned an exception has occured. 
     221 
     222     WARNING: this routine will change, to allow for generating coefficients 
     223     for a FUTURE 2d gradient generation. 
     224  */ 
    202225 
    203226  double 
    204     coefficients[9], 
    205     validity; 
    206  
    207   Image 
    208     *distort_image; 
    209  
    210   long 
    211     j, 
    212     y; 
     227    *coefficients; 
     228 
     229  register int 
     230    i; 
    213231 
    214232  MagickBooleanType 
    215233    status; 
    216234 
    217   PointInfo 
    218     point;          /* point to sample (center of filtered resample of area) */ 
    219  
    220   RectangleInfo 
    221     geometry; 
    222  
    223   register long 
    224     i, 
    225     x; 
    226  
    227   ResampleFilter 
    228     **resample_filter; 
    229  
    230   ViewInfo 
    231     **distort_view; 
    232  
    233   assert(image != (Image *) NULL); 
    234   assert(image->signature == MagickSignature); 
    235   if (image->debug != MagickFalse) 
    236     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 
    237   assert(exception != (ExceptionInfo *) NULL); 
    238   assert(exception->signature == MagickSignature); 
    239   (void) ResetMagickMemory(coefficients,0,sizeof(coefficients)); 
    240  
    241   /* 
    242     Convert input arguments into mapping coefficents for distortion 
     235  /* currently the array is always 9 doubles, that may change in the future */ 
     236  coefficients = AcquireQuantumMemory(9,sizeof(*coefficients)); 
     237  if (coefficients == (double *) NULL) { 
     238    (void) ThrowMagickException(exception,GetMagickModule(), 
     239                  ResourceLimitError,"MemoryAllocationFailed", 
     240                  "%s", "DistortCoefficients"); 
     241    return((double *) NULL); 
     242  } 
     243  /* zero out coeffiecents array */ 
     244  for (i=0; i < 9L; i++) 
     245    coefficients[i] = 0.0; 
     246 
     247  /* If not enough control point pairs are found for specific distortions 
     248    fall back to Affine distortion (allowing 0 to 3 point pairs) 
    243249  */ 
    244   switch (method) 
     250  if ( number_arguments < 16 && 
     251       (  *method == BilinearDistortion 
     252       || *method == PerspectiveDistortion 
     253       ) ) 
     254    *method = AffineDistortion; 
     255 
     256  switch (*method) 
    245257  { 
    246258    case AffineDistortion: 
    247259    { 
    248       double 
    249         **matrix, 
    250         **vectors, 
    251         terms[3]; 
    252  
    253       /* Affine Distortion 
    254             u =   c0*x + c2*y + c4 
    255             v =   c1*x + c3*y + c5 
    256  
    257          Input Arguments are pairs of distorted coodinates (minimum 3 sets) 
    258          Which will be used to generate the coefficients of the above. 
    259             u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3, ... 
     260      /* Input Arguments are any number of pairs of distorted coodinates 
     261         Which will be used to generate coefficients for Distortion. 
     262            u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ... 
    260263      */ 
    261       if (number_arguments < 12) { 
    262         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    263                      "InvalidArgument","%s : '%s'","distort Affine", 
    264                      "Minimum of 3 pairs of coords needed (12 numbers)"); 
    265         return((Image *) NULL); 
    266       } 
    267264      if (number_arguments%4 != 0) { 
    268265        (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    269                      "InvalidArgument","%s : '%s'","distort Affine", 
     266                     "InvalidArgument","%s : '%s'","distort Map", 
    270267                     "Requires pairs of coords (sX,sY,dX,dY)"); 
    271         return((Image *) NULL); 
    272       } 
    273       matrix = AcquireMagickMatrix(3UL,3UL); 
    274       vectors = AcquireMagickMatrix(2UL,3UL); 
    275       if (matrix == (double **) NULL || vectors == (double **) NULL) 
    276       { 
     268        RelinquishMagickMemory(coefficients); 
     269        return((double *) NULL); 
     270      } 
     271      /* handle special cases of not enough arguments */ 
     272      if ( number_arguments == 0 ) { 
     273        /* null distortion -- use No-Op Affine Distortion 
     274            coefficients: sx, rx, ry, sy, tx, ty 
     275        */ 
     276        *method = AffineDistortion; 
     277        coefficients[0] = 1.0; 
     278        coefficients[3] = 1.0; 
     279      } 
     280      else if ( number_arguments == 4 ) { 
     281        /* if only 1 pair of points - Affine Translation only 
     282            coefficients: sx, rx, ry, sy, tx, ty 
     283        */ 
     284        *method = AffineDistortion; 
     285        coefficients[0] = 1.0; 
     286        coefficients[3] = 1.0; 
     287        coefficients[4] = arguments[0] - arguments[2]; 
     288        coefficients[5] = arguments[1] - arguments[3]; 
     289      } 
     290      else { 
     291        /* 
     292           Affine Distortion 
     293              u =  c0*x + c2*y + c4 
     294              v =  c1*x + c3*y + c5 
     295        */ 
     296        double 
     297          **matrix, 
     298          **vectors, 
     299          terms[3]; 
     300 
     301        *method = AffineDistortion; 
     302        matrix = AcquireMagickMatrix(3UL,3UL); 
     303        vectors = AcquireMagickMatrix(2UL,3UL); 
     304        if (matrix == (double **) NULL || vectors == (double **) NULL) 
     305        { 
     306          matrix = RelinquishMagickMatrix(matrix, 3UL); 
     307          vectors = RelinquishMagickMatrix(vectors, 2UL); 
     308          coefficients = RelinquishMagickMemory(coefficients); 
     309          (void) ThrowMagickException(exception,GetMagickModule(), 
     310                  ResourceLimitError,"MemoryAllocationFailed", 
     311                  "%s", "DistortCoefficients"); 
     312          return((double *) NULL); 
     313        } 
     314        /* Add given control point pairs for least squares solving */ 
     315        for (i=0; i < (long) number_arguments; i+=4) { 
     316          terms[0] = arguments[i+2];  /* x */ 
     317          terms[1] = arguments[i+3];  /* y */ 
     318          terms[2] = 1;               /* 1 */ 
     319          LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i]),3UL,2UL); 
     320        } 
     321        if ( number_arguments == 8 ) { 
     322          /* Only two pairs were given, but we need 3 to solve the affine. 
     323             Fake a 3rd control point pair by p1 around p0 by 90 degrees 
     324             That is given    u0,v0 x0,y0   u1,v1 x1,y1 
     325             Then   u2 = u0 - (v1-v0)   v2 = v0 + (u1-u0) 
     326                    x2 = x0 - (y1-y0)   y2 = y0 + (x1-x0) 
     327          */ 
     328          double 
     329            uv2[2]; 
     330 
     331          uv2[0] = arguments[0] - arguments[5] + arguments[1];   /* u2 */ 
     332          uv2[1] = arguments[1] + arguments[4] + arguments[0];   /* v2 */ 
     333          terms[0] = arguments[2] - arguments[7] + arguments[3]; /* x2 */ 
     334          terms[1] = arguments[3] + arguments[6] + arguments[2]; /* y2 */ 
     335          terms[2] = 1;                                          /* 1 */ 
     336          LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL); 
     337        } 
     338        status=GaussJordanElimination(matrix,vectors,3UL,2UL); 
     339        if ( status == MagickTrue ) { 
     340          /* Affine is a bit weird in its ordering of coefficients. 
     341            This ordering is for backward compatibility with the older 
     342            affine functions of IM. 
     343 
     344            coefficients order: sx, rx, ry, sy, tx, ty 
     345            Where    u = sx*x + ry*y + tx 
     346                     v = rx*x + sy*y + ty 
     347          */ 
     348          coefficients[0] = vectors[0][0]; /* sx */ 
     349          coefficients[1] = vectors[1][0]; /* rx */ 
     350          coefficients[2] = vectors[0][1]; /* ry */ 
     351          coefficients[3] = vectors[1][1]; /* sy */ 
     352          coefficients[4] = vectors[0][2]; /* tx */ 
     353          coefficients[5] = vectors[1][2]; /* ty */ 
     354        } 
    277355        matrix = RelinquishMagickMatrix(matrix, 3UL); 
    278356        vectors = RelinquishMagickMatrix(vectors, 2UL); 
    279         ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 
    280         return((Image *) NULL); 
    281       } 
    282       /* Add control points for least squares solving */ 
    283       for (i=0; i < (long) number_arguments; i+=4) { 
    284         terms[0] = arguments[i+2];  /* x */ 
    285         terms[1] = arguments[i+3];  /* y */ 
    286         terms[2] = 1;               /* 1 */ 
    287         LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i]),3UL,2UL); 
    288       } 
    289       status=GaussJordanElimination(matrix,vectors,3UL,2UL); 
    290       matrix = RelinquishMagickMatrix(matrix, 3UL); 
    291       if ( status == MagickTrue ) { 
    292         /* Affine has a bit of a weird ordering of coefficents. 
    293            This ordering is for backward compatibility with the older 
    294            affine functions of IM. (See AffineProjection next) 
    295         */ 
    296         coefficients[0] = vectors[0][0]; /* u = ... */ 
    297         coefficients[2] = vectors[0][1]; 
    298         coefficients[4] = vectors[0][2]; 
    299         coefficients[1] = vectors[1][0]; /* v = ... */ 
    300         coefficients[3] = vectors[1][1]; 
    301         coefficients[5] = vectors[1][2]; 
    302       } 
    303       vectors = RelinquishMagickMatrix(vectors, 2UL); 
    304       if ( status == MagickFalse ) { 
    305         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    306                      "InvalidArgument","%s : '%s'","distort Affine", 
    307                      "Unsolvable Matrix"); 
    308         return((Image *) NULL); 
    309       } 
    310       break; 
     357        if ( status == MagickFalse ) { 
     358          coefficients = RelinquishMagickMemory(coefficients); 
     359          (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
     360                      "InvalidArgument","%s : '%s'","distort Affine", 
     361                      "Unsolvable Matrix"); 
     362          return((double *) NULL); 
     363        } 
     364      } 
     365      if ( GetImageArtifact(image,"distort:verbose") != (const char *) NULL ) { 
     366        double *inverse = AcquireQuantumMemory(8,sizeof(coefficients[i])); 
     367        if (inverse == (double *) NULL) { 
     368          coefficients = RelinquishMagickMemory(coefficients); 
     369          (void) ThrowMagickException(exception,GetMagickModule(), 
     370                  ResourceLimitError,"MemoryAllocationFailed", 
     371                  "%s", "DistortCoefficients"); 
     372          return((double *) NULL); 
     373        } 
     374        InvertAffineCoefficients(coefficients, inverse); 
     375        fprintf(stderr, "Affine ReverseMap\n"); 
     376        for (i=0; i<6; i++) 
     377          fprintf(stderr, "  %lf\n", coefficients[i]); 
     378        fprintf(stderr, "Affine Projection\n"); 
     379        for (i=0; i<6; i++) 
     380          fprintf(stderr, "  %lf\n", inverse[i]); 
     381        inverse = RelinquishMagickMemory(inverse); 
     382      } 
     383      return(coefficients); 
    311384    } 
    312385    case AffineProjectionDistortion: 
     
    314387      /* 
    315388        Arguments: Affine Matrix (forward mapping) 
    316            sx, rx, ry, sy, tx, ty 
     389        Arguments  sx, rx, ry, sy, tx, ty 
     390        Where      u = sx*x + ry*y + tx 
     391                   v = rx*x + sy*y + ty 
    317392      */ 
    318393      if (number_arguments != 6) { 
     394        coefficients = RelinquishMagickMemory(coefficients); 
    319395        (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    320396                     "InvalidArgument","%s : '%s'","distort AffineProjection", 
    321397                     "Needs 6 numbers"); 
    322         return((Image *) NULL); 
     398        return((double *) NULL); 
    323399      } 
    324400      InvertAffineCoefficients(arguments, coefficients); 
    325       break; 
    326     } 
    327     case BilinearDistortion: 
    328     { 
    329       double 
    330         **matrix, 
    331         *vectors[2], 
    332         terms[4]; 
    333  
    334       /* Bilinear Distortion (reversed) 
    335  
    336             u = c0*x + c1*y + c2*x*y + c3; 
    337  
    338             v = c4*x + c5*y + c6*x*y + c7; 
    339  
    340          Input Arguments are pairs of distorted coodinates (minimum 4 pairs) 
    341          Which will be used to generate the coefficients of the above. 
    342             u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ... 
    343       */ 
    344       if (number_arguments < 16) { 
    345         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    346                      "InvalidArgument","%s : '%s'","distort Bilinear", 
    347                      "Minimum of 4 pairs of coords needed (16 numbers)"); 
    348         return((Image *) NULL); 
    349       } 
    350       if (number_arguments%4 != 0) { 
    351         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    352                      "InvalidArgument","%s : '%s'","distort Bilinear", 
    353                      "Requires pairs of coords (sX,sY,dX,dY)"); 
    354         return((Image *) NULL); 
    355       } 
    356       matrix = AcquireMagickMatrix(4UL,4UL); 
    357       if (matrix == (double **) NULL) { 
    358         matrix = RelinquishMagickMatrix(matrix, 4UL); 
    359         ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 
    360         return((Image *) NULL); 
    361       } 
    362       /* fake a 2x3 vectors matrix from coefficients array */ 
    363       for (i=0; i < 8L; i++) 
    364         coefficients[i] = 0.0; 
    365       vectors[0] = &(coefficients[0]); 
    366       vectors[1] = &(coefficients[4]); 
    367       /* Add control points for least squares solving */ 
    368       for (i=0; i < (long) number_arguments; i+=4) { 
    369         terms[0] = arguments[i+2];     /*  x  */ 
    370         terms[1] = arguments[i+3];     /*  y  */ 
    371         terms[2] = terms[0]*terms[1];  /* x*y */ 
    372         terms[3] = 1;                  /*  1  */ 
    373         LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i]),4UL,2UL); 
    374       } 
    375       status=GaussJordanElimination(matrix,vectors,4UL,2UL); 
    376       matrix = RelinquishMagickMatrix(matrix, 4UL); 
    377       if ( status == MagickFalse ) { 
    378         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    379                      "InvalidArgument","%s : '%s'","distort Bilinear", 
    380                      "Unsolvable Matrix"); 
    381         return((Image *) NULL); 
    382       } 
    383       break; 
     401      if ( GetImageArtifact(image,"distort:verbose") != (const char *) NULL ) { 
     402        fprintf(stderr, "Affine ReverseMap\n"); 
     403        for (i=0; i<6; i++) 
     404          fprintf(stderr, "  %lf\n", coefficients[i]); 
     405        fprintf(stderr, "AffineProjection\n"); 
     406        for (i=0; i<6; i++) 
     407          fprintf(stderr, "  %lf\n", arguments[i]); 
     408      } 
     409      return(coefficients); 
    384410    } 
    385411    case PerspectiveDistortion: 
    386     { 
     412    { /* 
     413         Perspective Distortion (a ratio of affine distortions) 
     414 
     415                p()     c0*x + c1*y + c2 
     416            u = ----- = ------------------ 
     417                r()     c6*x + c7*y + 1 
     418 
     419                q()     c3*x + c4*y + c5 
     420            v = ----- = ------------------ 
     421                r()     c6*x + c7*y + 1 
     422 
     423           c8 = Sign of 'r', or the denominator affine, for the actual image. 
     424                This determines what part of the distorted image is 'ground' 
     425                side of the horizon, the other part is 'sky' or invalid. 
     426                Valid values are  +1.0  or  -1.0  only. 
     427 
     428         WARNING: Perspective Equations are linked (not separatable) as c6 
     429         and c7 are shared by both equations.  All 8 coefficients need to be 
     430         determined simultaneously. 
     431      */ 
    387432      double 
    388433        **matrix, 
     
    390435        terms[8]; 
    391436 
    392       /* Perspective Distortion (a ratio of affine distortions) 
    393  
    394                  p()     c0*x + c1*y + c2 
    395             u = ----- = ------------------ 
    396                  r()     c6*x + c7*y + 1 
    397  
    398                  q()     c3*x + c4*y + c5 
    399             v = ----- = ------------------ 
    400                  r()     c6*x + c7*y + 1 
    401  
    402             c8 = Sign of 'r', or the denominator affine, for the actual image. 
    403                  This determines what part of the distorted image is 'ground' 
    404                  side of the horizon, the other part is 'sky' or invalid. 
    405  
    406          WARNING: Equations are linked, and not separable. 
    407  
    408          Input Arguments are pairs of distorted coodinates (minimum 4 pairs) 
    409          Which will be used to generate the coefficients of the above. 
    410             u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ... 
    411       */ 
    412       if (number_arguments < 16) { 
    413         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    414                      "InvalidArgument","%s : '%s'","distort Bilinear", 
    415                      "Minimum of 4 pairs of coords needed (16 numbers)"); 
    416         return((Image *) NULL); 
    417       } 
    418       if (number_arguments%4 != 0) { 
    419         (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    420                      "InvalidArgument","%s : '%s'","distort Bilinear", 
    421                      "Requires pairs of coords (sX,sY,dX,dY)"); 
    422         return((Image *) NULL); 
    423       } 
     437      *method = PerspectiveDistortion; 
     438      /* fake 1x8 vectors matrix directly from coefficients array */ 
     439      vectors[0] = &(coefficients[0]); 
     440      /* 8x8 least-squares matrix (zeroed) */ 
    424441      matrix = AcquireMagickMatrix(8UL,8UL); 
    425442      if (matrix == (double **) NULL) { 
    426         matrix = RelinquishMagickMatrix(matrix, 8UL); 
    427         ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 
    428         return((Image *) NULL); 
    429       } 
    430       /* fake a 1x8 vectors matrix from coefficients array */ 
    431       for (i=0; i < 8L; i++) 
    432         coefficients[i] = 0.0; 
    433       vectors[0] = &(coefficients[0]); 
     443        (void) ThrowMagickException(exception,GetMagickModule(), 
     444                  ResourceLimitError,"MemoryAllocationFailed", 
     445                  "%s", "DistortCoefficients"); 
     446        return((double *) NULL); 
     447      } 
    434448      /* Add control points for least squares solving */ 
    435449      for (i=0; i < (long) number_arguments; i+=4) { 
     
    443457        terms[7]=-terms[1]*arguments[i];  /* 1/(c7*y) */ 
    444458        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i]), 
    445              8UL,1UL); 
     459            8UL,1UL); 
    446460 
    447461        terms[0]=0.0; 
     
    454468        terms[7]=-terms[4]*arguments[i+1];  /* 1/(c7*y) */ 
    455469        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+1]), 
    456              8UL,1UL); 
     470            8UL,1UL); 
    457471      } 
    458472      status=GaussJordanElimination(matrix,vectors,8UL,1UL); 
    459473      matrix = RelinquishMagickMatrix(matrix, 8UL); 
    460474      if ( status == MagickFalse ) { 
     475        coefficients = RelinquishMagickMemory(coefficients); 
    461476        (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
    462                      "InvalidArgument","%s : '%s'","distort Prespective", 
    463                      "Unsolvable Matrix"); 
    464         return((Image *) NULL); 
     477                    "InvalidArgument","%s : '%s'","distort Prespective", 
     478                    "Unsolvable Matrix"); 
     479        return((double *) NULL); 
    465480      } 
    466481      /* 
     
    471486      */ 
    472487      coefficients[8] = coefficients[6]*arguments[2] 
    473                            + coefficients[7]*arguments[3] + 1.0; 
     488                          + coefficients[7]*arguments[3] + 1.0; 
    474489      coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0; 
    475       break; 
     490 
     491      if ( GetImageArtifact(image,"distort:verbose") != (const char *) NULL ) { 
     492        double *inverse = AcquireQuantumMemory(8,sizeof(coefficients[i])); 
     493        if (inverse == (double *) NULL) { 
     494          coefficients = RelinquishMagickMemory(coefficients); 
     495          (void) ThrowMagickException(exception,GetMagickModule(), 
     496                  ResourceLimitError,"MemoryAllocationFailed", 
     497                  "%s", "DistortCoefficients"); 
     498          return((double *) NULL); 
     499        } 
     500        InvertPerspectiveCoefficients(coefficients, inverse); 
     501        fprintf(stderr, "Perspective ReverseMap\n"); 
     502        for (i=0; i<8; i++) 
     503          fprintf(stderr, "  %lf\n", coefficients[i]); 
     504        fprintf(stderr, "PerspectiveProjection\n"); 
     505        for (i=0; i<8; i++) 
     506          fprintf(stderr, "  %lf\n", inverse[i]); 
     507        inverse = RelinquishMagickMemory(inverse); 
     508      } 
     509      return(coefficients); 
    476510    } 
    477511    case PerspectiveProjectionDistortion: 
     
    484518                     "InvalidArgument","%s : '%s'", 
    485519                     "distort PerspectiveProjection", "Needs 8 numbers"); 
    486         return((Image *) NULL); 
     520        return((double *) NULL); 
    487521      } 
    488522      InvertPerspectiveCoefficients(arguments, coefficients); 
     523      if ( GetImageArtifact(image,"distort:verbose") != (const char *) NULL ) { 
     524        fprintf(stderr, "Perspective ReverseMap\n"); 
     525        for (i=0; i<8; i++) 
     526          fprintf(stderr, "  %lf\n", coefficients[i]); 
     527        fprintf(stderr, "PerspectiveProjection\n"); 
     528        for (i=0; i<8; i++) 
     529          fprintf(stderr, "  %lf\n", arguments[i]); 
     530      } 
    489531      /* 
    490532        Calculate 9'th coefficient! The ground-sky determination. 
     
    492534        Just use any valid image cocodinate in destination for determination. 
    493535        For a forward mapped perspective the images 0,0 coord will map to 
    494         c2,c5 in the distorted image, so get the sign of denominator of that. 
     536        c2,c5 in the distorted image, so set the sign of denominator of that. 
    495537      */ 
    496538      coefficients[8] = coefficients[6]*arguments[2] 
    497539                           + coefficients[7]*arguments[5] + 1.0; 
    498540      coefficients[8] = (coefficients[8] < 0.0) ? -1.0 : +1.0; 
    499       break; 
     541      return(coefficients); 
     542    } 
     543    case BilinearDistortion: 
     544    { 
     545      double 
     546        **matrix, 
     547        *vectors[2], 
     548        terms[4]; 
     549 
     550      /* Bilinear Distortion (currently reversed -- this needs to be fixed) 
     551            u = c0*x + c1*y + c2*x*y + c3; 
     552            v = c4*x + c5*y + c6*x*y + c7; 
     553 
     554         Input Arguments are pairs of distorted coodinates (minimum 4 pairs) 
     555         Which will be used to generate the coefficients of the above. 
     556            u1,v1, x1,y1,  u2,v2, x2,y2,  u3,v3, x3,y3,  u4,v4, x4,y4 ... 
     557      */ 
     558      if (number_arguments%4 != 0) { 
     559        coefficients = RelinquishMagickMemory(coefficients); 
     560        (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
     561                     "InvalidArgument","%s : '%s'","distort Bilinear", 
     562                     "Requires pairs of coords (sX,sY,dX,dY)"); 
     563        return((double *) NULL); 
     564      } 
     565      matrix = AcquireMagickMatrix(4UL,4UL); 
     566      if (matrix == (double **) NULL) { 
     567        coefficients = RelinquishMagickMemory(coefficients); 
     568        (void) ThrowMagickException(exception,GetMagickModule(), 
     569                  ResourceLimitError,"MemoryAllocationFailed", 
     570                  "%s", "DistortCoefficients"); 
     571        return((double *) NULL); 
     572      } 
     573      /* fake a 2x3 vectors matrix from coefficients array */ 
     574      vectors[0] = &(coefficients[0]); 
     575      vectors[1] = &(coefficients[4]); 
     576      /* Add control points for least squares solving */ 
     577      for (i=0; i < (long) number_arguments; i+=4) { 
     578        terms[0] = arguments[i+2];     /*  x  */ 
     579        terms[1] = arguments[i+3];     /*  y  */ 
     580        terms[2] = terms[0]*terms[1];  /* x*y */ 
     581        terms[3] = 1;                  /*  1  */ 
     582        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i]),4UL,2UL); 
     583      } 
     584      status=GaussJordanElimination(matrix,vectors,4UL,2UL); 
     585      matrix = RelinquishMagickMatrix(matrix, 4UL); 
     586      if ( status == MagickFalse ) { 
     587        coefficients = RelinquishMagickMemory(coefficients); 
     588        (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 
     589                     "InvalidArgument","%s : '%s'","distort Bilinear", 
     590                     "Unsolvable Matrix"); 
     591        return((double *) NULL); 
     592      } 
     593      return(coefficients); 
    500594    } 
    501595    case ScaleRotateTranslateDistortion: 
     
    530624      switch ( number_arguments ) { 
    531625      case 0: