2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % DDDD IIIII SSSSS TTTTT OOO RRRR TTTTT %
7 % D D I SS T O O R R T %
8 % D D I SSS T O O RRRR T %
9 % D D I SS T O O R R T %
10 % DDDD IIIII SSSSS T OOO R R T %
13 % MagickCore Image Distortion Methods %
21 % Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/cache.h"
46 #include "magick/cache-view.h"
47 #include "magick/colorspace-private.h"
48 #include "magick/composite-private.h"
49 #include "magick/distort.h"
50 #include "magick/exception.h"
51 #include "magick/exception-private.h"
52 #include "magick/gem.h"
53 #include "magick/hashmap.h"
54 #include "magick/image.h"
55 #include "magick/list.h"
56 #include "magick/matrix.h"
57 #include "magick/memory_.h"
58 #include "magick/monitor-private.h"
59 #include "magick/pixel.h"
60 #include "magick/pixel-private.h"
61 #include "magick/resample.h"
62 #include "magick/resample-private.h"
63 #include "magick/registry.h"
64 #include "magick/semaphore.h"
65 #include "magick/string_.h"
66 #include "magick/thread-private.h"
67 #include "magick/token.h"
70 Numerous internal routines for image distortions.
72 static inline double MagickMin(const double x,const double y)
74 return( x < y ? x : y);
76 static inline double MagickMax(const double x,const double y)
78 return( x > y ? x : y);
81 static inline void AffineArgsToCoefficients(double *affine)
83 /* map external sx,ry,rx,sy,tx,ty to internal c0,c2,c4,c1,c3,c5 */
84 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
85 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
86 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
88 static inline void CoefficientsToAffineArgs(double *coeff)
90 /* map internal c0,c1,c2,c3,c4,c5 to external sx,ry,rx,sy,tx,ty */
91 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
92 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
93 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
95 static void InvertAffineCoefficients(const double *coeff,double *inverse)
97 /* From "Digital Image Warping" by George Wolberg, page 50 */
100 determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
101 inverse[0]=determinant*coeff[4];
102 inverse[1]=determinant*(-coeff[1]);
103 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
104 inverse[3]=determinant*(-coeff[3]);
105 inverse[4]=determinant*coeff[0];
106 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
109 static void InvertPerspectiveCoefficients(const double *coeff,
112 /* From "Digital Image Warping" by George Wolberg, page 53 */
115 determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
116 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
117 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
118 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
119 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
120 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
121 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
122 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
123 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
126 static inline double MagickRound(double x)
128 /* round the fraction to nearest integer */
130 return((double) ((long) (x+0.5)));
131 return((double) ((long) (x-0.5)));
135 * Polynomial Term Defining Functions
137 * Order must either be an integer, or 1.5 to produce
138 * the 2 number_valuesal polyminal function...
139 * affine 1 (3) u = c0 + c1*x + c2*y
140 * bilinear 1.5 (4) u = '' + c3*x*y
141 * quadratic 2 (6) u = '' + c4*x*x + c5*y*y
142 * cubic 3 (10) u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
143 * quartic 4 (15) u = '' + c10*x^4 + ... + c14*y^4
144 * quintic 5 (21) u = '' + c15*x^5 + ... + c20*y^5
145 * number in parenthesis minimum number of points needed.
146 * Anything beyond quintic, has not been implemented until
147 * a more automated way of determined terms is found.
149 * Note the slight re-ordering of the terms for a quadratic polynomial
150 * which is to allow the use of a bi-linear (order=1.5) polynomial.
151 * All the later polynomials are ordered simply from x^N to y^N
153 static unsigned long poly_number_terms(double order)
155 /* Return the number of terms for a 2d polynomial */
156 if ( order < 1 || order > 5 ||
157 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
158 return 0; /* invalid polynomial order */
159 return((unsigned long) floor((order+1)*(order+2)/2));
162 static double poly_basis_fn(long n, double x, double y)
164 /* Return the result for this polynomial term */
166 case 0: return( 1.0 ); /* constant */
168 case 2: return( y ); /* affine order = 1 terms = 3 */
169 case 3: return( x*y ); /* bilinear order = 1.5 terms = 4 */
170 case 4: return( x*x );
171 case 5: return( y*y ); /* quadratic order = 2 terms = 6 */
172 case 6: return( x*x*x );
173 case 7: return( x*x*y );
174 case 8: return( x*y*y );
175 case 9: return( y*y*y ); /* cubic order = 3 terms = 10 */
176 case 10: return( x*x*x*x );
177 case 11: return( x*x*x*y );
178 case 12: return( x*x*y*y );
179 case 13: return( x*y*y*y );
180 case 14: return( y*y*y*y ); /* quartic order = 4 terms = 15 */
181 case 15: return( x*x*x*x*x );
182 case 16: return( x*x*x*x*y );
183 case 17: return( x*x*x*y*y );
184 case 18: return( x*x*y*y*y );
185 case 19: return( x*y*y*y*y );
186 case 20: return( y*y*y*y*y ); /* quintic order = 5 terms = 21 */
188 return( 0 ); /* should never happen */
190 static const char *poly_basis_str(long n)
192 /* return the result for this polynomial term */
194 case 0: return(""); /* constant */
195 case 1: return("*ii");
196 case 2: return("*jj"); /* affiine order = 1 terms = 3 */
197 case 3: return("*ii*jj"); /* biiliinear order = 1.5 terms = 4 */
198 case 4: return("*ii*ii");
199 case 5: return("*jj*jj"); /* quadratiic order = 2 terms = 6 */
200 case 6: return("*ii*ii*ii");
201 case 7: return("*ii*ii*jj");
202 case 8: return("*ii*jj*jj");
203 case 9: return("*jj*jj*jj"); /* cubiic order = 3 terms = 10 */
204 case 10: return("*ii*ii*ii*ii");
205 case 11: return("*ii*ii*ii*jj");
206 case 12: return("*ii*ii*jj*jj");
207 case 13: return("*ii*jj*jj*jj");
208 case 14: return("*jj*jj*jj*jj"); /* quartiic order = 4 terms = 15 */
209 case 15: return("*ii*ii*ii*ii*ii");
210 case 16: return("*ii*ii*ii*ii*jj");
211 case 17: return("*ii*ii*ii*jj*jj");
212 case 18: return("*ii*ii*jj*jj*jj");
213 case 19: return("*ii*jj*jj*jj*jj");
214 case 20: return("*jj*jj*jj*jj*jj"); /* quiintiic order = 5 terms = 21 */
216 return( "UNKNOWN" ); /* should never happen */
218 static double poly_basis_dx(long n, double x, double y)
220 /* polynomial term for x derivative */
222 case 0: return( 0.0 ); /* constant */
223 case 1: return( 1.0 );
224 case 2: return( 0.0 ); /* affine order = 1 terms = 3 */
225 case 3: return( y ); /* bilinear order = 1.5 terms = 4 */
227 case 5: return( 0.0 ); /* quadratic order = 2 terms = 6 */
228 case 6: return( x*x );
229 case 7: return( x*y );
230 case 8: return( y*y );
231 case 9: return( 0.0 ); /* cubic order = 3 terms = 10 */
232 case 10: return( x*x*x );
233 case 11: return( x*x*y );
234 case 12: return( x*y*y );
235 case 13: return( y*y*y );
236 case 14: return( 0.0 ); /* quartic order = 4 terms = 15 */
237 case 15: return( x*x*x*x );
238 case 16: return( x*x*x*y );
239 case 17: return( x*x*y*y );
240 case 18: return( x*y*y*y );
241 case 19: return( y*y*y*y );
242 case 20: return( 0.0 ); /* quintic order = 5 terms = 21 */
244 return( 0.0 ); /* should never happen */
246 static double poly_basis_dy(long n, double x, double y)
248 /* polynomial term for y derivative */
250 case 0: return( 0.0 ); /* constant */
251 case 1: return( 0.0 );
252 case 2: return( 1.0 ); /* affine order = 1 terms = 3 */
253 case 3: return( x ); /* bilinear order = 1.5 terms = 4 */
254 case 4: return( 0.0 );
255 case 5: return( y ); /* quadratic order = 2 terms = 6 */
256 default: return( poly_basis_dx(n-1,x,y) ); /* weird but true */
258 /* NOTE: the only reason that last is not true for 'quadtratic'
259 is due to the re-arrangement of terms to allow for 'bilinear'
264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
268 + G e n e r a t e C o e f f i c i e n t s %
272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274 % GenerateCoefficients() takes user provided input arguments and generates
275 % the coefficients, needed to apply the specific distortion for either
276 % distorting images (generally using control points) or generating a color
277 % gradient from sparsely separated color points.
279 % The format of the GenerateCoefficients() method is:
281 % Image *GenerateCoefficients(const Image *image,DistortImageMethod method,
282 % const unsigned long number_arguments,const double *arguments,
283 % unsigned long number_values, ExceptionInfo *exception)
285 % A description of each parameter follows:
287 % o image: the image to be distorted.
289 % o method: the method of image distortion/ sparse gradient
291 % o number_arguments: the number of arguments given.
293 % o arguments: the arguments for this distortion method.
295 % o number_values: the style and format of given control points, (caller type)
296 % 0: 2 dimensional mapping of control points (Distort)
297 % Format: u,v,x,y where u,v is the 'source' of the
298 % the color to be plotted, for DistortImage()
299 % N: Interpolation of control points with N values (usally r,g,b)
300 % Format: x,y,r,g,b mapping x,y to color values r,g,b
301 % IN future, varible number of values may be given (1 to N)
303 % o exception: return any errors or warnings in this structure
305 % Note that the returned array of double values must be freed by the
306 % calling method using RelinquishMagickMemory(). This however may change in
307 % the future to require a more 'method' specific method.
309 % Because of this this method should not be classed as stable or used
310 % outside other MagickCore library methods.
313 static double *GenerateCoefficients(const Image *image,
314 DistortImageMethod *method,const unsigned long number_arguments,
315 const double *arguments,unsigned long number_values,ExceptionInfo *exception)
320 register unsigned long
324 number_coeff, /* number of coefficients to return (array size) */
325 cp_size, /* number floating point numbers per control point */
326 cp_x,cp_y, /* the x,y indexes for control point */
327 cp_values; /* index of values for this control point */
328 /* number_values Number of values given per control point */
330 if ( number_values == 0 ) {
331 /* Image distortion using control points (or other distortion)
332 That is generate a mapping so that x,y->u,v given u,v,x,y
334 number_values = 2; /* special case: two values of u,v */
335 cp_values = 0; /* the values i,j are BEFORE the destination CP x,y */
336 cp_x = 2; /* location of x,y in input control values */
338 /* NOTE: cp_values, also used for later 'reverse map distort' tests */
341 cp_x = 0; /* location of x,y in input control values */
343 cp_values = 2; /* and the other values are after x,y */
344 /* Typically in this case the values are R,G,B color values */
346 cp_size = number_values+2; /* each CP defintion involves this many numbers */
348 /* If not enough control point pairs are found for specific distortions
349 fall back to Affine distortion (allowing 0 to 3 point pairs)
351 if ( number_arguments < 4*cp_size &&
352 ( *method == BilinearForwardDistortion
353 || *method == BilinearReverseDistortion
354 || *method == PerspectiveDistortion
356 *method = AffineDistortion;
360 case AffineDistortion:
361 /* also BarycentricColorInterpolate: */
362 number_coeff=3*number_values;
364 case PolynomialDistortion:
365 /* number of coefficents depend on the given polynomal 'order' */
366 if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
368 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
369 "InvalidArgument","%s : '%s'","Polynomial",
370 "Invalid number of args: order [CPs]...");
371 return((double *) NULL);
373 i = poly_number_terms(arguments[0]);
374 number_coeff = 2 + i*number_values;
376 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
377 "InvalidArgument","%s : '%s'","Polynomial",
378 "Invalid order, should be 1 to 5, or 1.5");
379 return((double *) NULL);
381 if ( number_arguments < 1+i*cp_size ) {
382 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
383 "InvalidArgument", "%s : 'require at least %ld CPs'",
385 return((double *) NULL);
388 case BilinearReverseDistortion:
389 number_coeff=4*number_values;
392 The rest are constants as they are only used for image distorts
394 case BilinearForwardDistortion:
395 number_coeff=10; /* 2*4 coeff plus 2 constants */
396 cp_x = 0; /* Reverse src/dest coords for forward mapping */
401 case QuadraterialDistortion:
402 number_coeff=19; /* BilinearForward + BilinearReverse */
405 case ShepardsDistortion:
406 case VoronoiColorInterpolate:
407 number_coeff=1; /* not used, but provide some type of return */
412 case ScaleRotateTranslateDistortion:
413 case AffineProjectionDistortion:
416 case PolarDistortion:
417 case DePolarDistortion:
420 case PerspectiveDistortion:
421 case PerspectiveProjectionDistortion:
424 case BarrelDistortion:
425 case BarrelInverseDistortion:
428 case UndefinedDistortion:
430 assert(! "Unknown Method Given"); /* just fail assertion */
433 /* allocate the array of coefficients needed */
434 coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
435 if (coeff == (double *) NULL) {
436 (void) ThrowMagickException(exception,GetMagickModule(),
437 ResourceLimitError,"MemoryAllocationFailed",
438 "%s", "GenerateCoefficients");
439 return((double *) NULL);
442 /* zero out coeffiecents array */
443 for (i=0; i < number_coeff; i++)
448 case AffineDistortion:
452 for each 'value' given
454 Input Arguments are sets of control points...
455 For Distort Images u,v, x,y ...
456 For Sparse Gradients x,y, r,g,b ...
458 if ( number_arguments%cp_size != 0 ||
459 number_arguments < cp_size ) {
460 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
461 "InvalidArgument", "%s : 'require at least %ld CPs'",
463 coeff=(double *) RelinquishMagickMemory(coeff);
464 return((double *) NULL);
466 /* handle special cases of not enough arguments */
467 if ( number_arguments == cp_size ) {
468 /* Only 1 CP Set Given */
469 if ( cp_values == 0 ) {
470 /* image distortion - translate the image */
472 coeff[2] = arguments[0] - arguments[2];
474 coeff[5] = arguments[1] - arguments[3];
477 /* sparse gradient - use the values directly */
478 for (i=0; i<number_values; i++)
479 coeff[i*3+2] = arguments[cp_values+i];
483 /* 2 or more points (usally 3) given.
484 Solve a least squares simultanious equation for coefficients.
494 /* create matrix, and a fake vectors matrix */
495 matrix = AcquireMagickMatrix(3UL,3UL);
496 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
497 if (matrix == (double **) NULL || vectors == (double **) NULL)
499 matrix = RelinquishMagickMatrix(matrix, 3UL);
500 vectors = (double **) RelinquishMagickMemory(vectors);
501 coeff = (double *) RelinquishMagickMemory(coeff);
502 (void) ThrowMagickException(exception,GetMagickModule(),
503 ResourceLimitError,"MemoryAllocationFailed",
504 "%s", "DistortCoefficients");
505 return((double *) NULL);
507 /* fake a number_values x3 vectors matrix from coefficients array */
508 for (i=0; i < number_values; i++)
509 vectors[i] = &(coeff[i*3]);
510 /* Add given control point pairs for least squares solving */
511 for (i=0; i < number_arguments; i+=cp_size) {
512 terms[0] = arguments[i+cp_x]; /* x */
513 terms[1] = arguments[i+cp_y]; /* y */
514 terms[2] = 1; /* 1 */
515 LeastSquaresAddTerms(matrix,vectors,terms,
516 &(arguments[i+cp_values]),3UL,number_values);
518 if ( number_arguments == 2*cp_size ) {
519 /* Only two pairs were given, but we need 3 to solve the affine.
520 Fake extra coordinates by rotating p1 around p0 by 90 degrees.
521 x2 = x0 - (y1-y0) y2 = y0 + (x1-x0)
523 terms[0] = arguments[cp_x]
524 - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
525 terms[1] = arguments[cp_y] +
526 + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
527 terms[2] = 1; /* 1 */
528 if ( cp_values == 0 ) {
529 /* Image Distortion - rotate the u,v coordients too */
532 uv2[0] = arguments[0] - arguments[5] + arguments[1]; /* u2 */
533 uv2[1] = arguments[1] + arguments[4] - arguments[0]; /* v2 */
534 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
537 /* Sparse Gradient - use values of p0 for linear gradient */
538 LeastSquaresAddTerms(matrix,vectors,terms,
539 &(arguments[cp_values]),3UL,number_values);
542 /* Solve for LeastSquares Coefficients */
543 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
544 matrix = RelinquishMagickMatrix(matrix, 3UL);
545 vectors = (double **) RelinquishMagickMemory(vectors);
546 if ( status == MagickFalse ) {
547 coeff = (double *) RelinquishMagickMemory(coeff);
548 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
549 "InvalidArgument","%s : '%s'","Affine",
550 "Unsolvable Matrix");
551 return((double *) NULL);
556 case AffineProjectionDistortion:
559 Arguments: Affine Matrix (forward mapping)
560 Arguments sx, rx, ry, sy, tx, ty
561 Where u = sx*x + ry*y + tx
564 Returns coefficients (in there inverse form) ordered as...
567 AffineProjection Distortion Notes...
568 + Will only work with a 2 number_values for Image Distortion
569 + Can not be used for generating a sparse gradient (interpolation)
572 if (number_arguments != 6) {
573 coeff = (double *) RelinquishMagickMemory(coeff);
574 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
575 "InvalidArgument","%s : '%s'","AffineProjection",
576 "Needs 6 coeff values");
577 return((double *) NULL);
579 /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinate = 0, no inverse) */
580 for(i=0; i<6UL; i++ )
581 inverse[i] = arguments[i];
582 AffineArgsToCoefficients(inverse); /* map into coefficents */
583 InvertAffineCoefficients(inverse, coeff); /* invert */
584 *method = AffineDistortion;
588 case ScaleRotateTranslateDistortion:
590 /* Scale, Rotate and Translate Distortion
591 An alturnative Affine Distortion
592 Argument options, by number of arguments given:
593 7: x,y, sx,sy, a, nx,ny
600 Where actions are (in order of application)
601 x,y 'center' of transforms (default = image center)
602 sx,sy scale image by this amount (default = 1)
603 a angle of rotation (argument required)
604 nx,ny move 'center' here (default = x,y or no movement)
605 And convert to affine mapping coefficients
607 ScaleRotateTranslate Distortion Notes...
608 + Does not use a set of CPs in any normal way
609 + Will only work with a 2 number_valuesal Image Distortion
610 + Can not be used for generating a sparse gradient (interpolation)
616 /* set default center, and default scale */
617 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
618 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
620 switch ( number_arguments ) {
622 coeff = (double *) RelinquishMagickMemory(coeff);
623 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
624 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
625 "Needs at least 1 argument");
626 return((double *) NULL);
631 sx = sy = arguments[0];
635 x = nx = arguments[0];
636 y = ny = arguments[1];
637 switch ( number_arguments ) {
642 sx = sy = arguments[2];
651 sx = sy = arguments[2];
664 coeff = (double *) RelinquishMagickMemory(coeff);
665 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
666 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
667 "Too Many Arguments (7 or less)");
668 return((double *) NULL);
672 /* Trap if sx or sy == 0 -- image is scaled out of existance! */
673 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
674 coeff = (double *) RelinquishMagickMemory(coeff);
675 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
676 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
678 return((double *) NULL);
680 /* Save the given arguments as an affine distortion */
681 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
683 *method = AffineDistortion;
686 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
689 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
692 case PerspectiveDistortion:
694 Perspective Distortion (a ratio of affine distortions)
696 p(x,y) c0*x + c1*y + c2
697 u = ------ = ------------------
698 r(x,y) c6*x + c7*y + 1
700 q(x,y) c3*x + c4*y + c5
701 v = ------ = ------------------
702 r(x,y) c6*x + c7*y + 1
704 c8 = Sign of 'r', or the denominator affine, for the actual image.
705 This determines what part of the distorted image is 'ground'
706 side of the horizon, the other part is 'sky' or invalid.
707 Valid values are +1.0 or -1.0 only.
709 Input Arguments are sets of control points...
710 For Distort Images u,v, x,y ...
711 For Sparse Gradients x,y, r,g,b ...
713 Perspective Distortion Notes...
714 + Can be thought of as ratio of 3 affine transformations
715 + Not separatable: r() or c6 and c7 are used by both equations
716 + All 8 coefficients must be determined simultaniously
717 + Will only work with a 2 number_valuesal Image Distortion
718 + Can not be used for generating a sparse gradient (interpolation)
719 + It is not linear, but is simple to generate an inverse
720 + All lines within an image remain lines.
721 + but distances between points may vary.
735 /* fake 1x8 vectors matrix directly using the coefficients array */
736 vectors[0] = &(coeff[0]);
737 /* 8x8 least-squares matrix (zeroed) */
738 matrix = AcquireMagickMatrix(8UL,8UL);
739 if (matrix == (double **) NULL) {
740 (void) ThrowMagickException(exception,GetMagickModule(),
741 ResourceLimitError,"MemoryAllocationFailed",
742 "%s", "DistortCoefficients");
743 return((double *) NULL);
745 /* Add control points for least squares solving */
746 for (i=0; i < number_arguments; i+=4) {
747 terms[0]=arguments[i+cp_x]; /* c0*x */
748 terms[1]=arguments[i+cp_y]; /* c1*y */
749 terms[2]=1.0; /* c2*1 */
753 terms[6]=-terms[0]*arguments[i+cp_u]; /* 1/(c6*x) */
754 terms[7]=-terms[1]*arguments[i+cp_u]; /* 1/(c7*y) */
755 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
761 terms[3]=arguments[i+cp_x]; /* c3*x */
762 terms[4]=arguments[i+cp_y]; /* c4*y */
763 terms[5]=1.0; /* c5*1 */
764 terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
765 terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
766 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
769 /* Solve for LeastSquares Coefficients */
770 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
771 matrix = RelinquishMagickMatrix(matrix, 8UL);
772 if ( status == MagickFalse ) {
773 coeff = (double *) RelinquishMagickMemory(coeff);
774 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
775 "InvalidArgument","%s : '%s'","Perspective",
776 "Unsolvable Matrix");
777 return((double *) NULL);
780 Calculate 9'th coefficient! The ground-sky determination.
781 What is sign of the 'ground' in r() denominator affine function?
782 Just use any valid image coordinate (first control point) in
783 destination for determination of what part of view is 'ground'.
785 coeff[8] = coeff[6]*arguments[cp_x]
786 + coeff[7]*arguments[cp_y] + 1.0;
787 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
791 case PerspectiveProjectionDistortion:
794 Arguments: Perspective Coefficents (forward mapping)
796 if (number_arguments != 8) {
797 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
798 "InvalidArgument","%s : '%s'","PerspectiveProjection",
799 "Needs 8 coefficient values");
800 return((double *) NULL);
802 /* FUTURE: trap test c0*c4-c3*c1 == 0 (determinate = 0, no inverse) */
803 InvertPerspectiveCoefficients(arguments, coeff);
805 Calculate 9'th coefficient! The ground-sky determination.
806 What is sign of the 'ground' in r() denominator affine function?
807 Just use any valid image cocodinate in destination for determination.
808 For a forward mapped perspective the images 0,0 coord will map to
809 c2,c5 in the distorted image, so set the sign of denominator of that.
811 coeff[8] = coeff[6]*arguments[2]
812 + coeff[7]*arguments[5] + 1.0;
813 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
814 *method = PerspectiveDistortion;
818 case BilinearForwardDistortion:
819 case BilinearReverseDistortion:
821 /* Bilinear Distortion (Forward mapping)
822 v = c0*x + c1*y + c2*x*y + c3;
823 for each 'value' given
825 This is actually a simple polynomial Distortion! The difference
826 however is when we need to reverse the above equation to generate a
827 BilinearForwardDistortion (see below).
829 Input Arguments are sets of control points...
830 For Distort Images u,v, x,y ...
831 For Sparse Gradients x,y, r,g,b ...
842 /* create matrix, and a fake vectors matrix */
843 matrix = AcquireMagickMatrix(4UL,4UL);
844 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
845 if (matrix == (double **) NULL || vectors == (double **) NULL)
847 matrix = RelinquishMagickMatrix(matrix, 4UL);
848 vectors = (double **) RelinquishMagickMemory(vectors);
849 coeff = (double *) RelinquishMagickMemory(coeff);
850 (void) ThrowMagickException(exception,GetMagickModule(),
851 ResourceLimitError,"MemoryAllocationFailed",
852 "%s", "DistortCoefficients");
853 return((double *) NULL);
855 /* fake a number_values x4 vectors matrix from coefficients array */
856 for (i=0; i < number_values; i++)
857 vectors[i] = &(coeff[i*4]);
858 /* Add given control point pairs for least squares solving */
859 for (i=0; i < number_arguments; i+=cp_size) {
860 terms[0] = arguments[i+cp_x]; /* x */
861 terms[1] = arguments[i+cp_y]; /* y */
862 terms[2] = terms[0]*terms[1]; /* x*y */
863 terms[3] = 1; /* 1 */
864 LeastSquaresAddTerms(matrix,vectors,terms,
865 &(arguments[i+cp_values]),4UL,number_values);
867 /* Solve for LeastSquares Coefficients */
868 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
869 matrix = RelinquishMagickMatrix(matrix, 4UL);
870 vectors = (double **) RelinquishMagickMemory(vectors);
871 if ( status == MagickFalse ) {
872 coeff = (double *) RelinquishMagickMemory(coeff);
873 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
874 "InvalidArgument","%s : '%s'",
875 *method == BilinearForwardDistortion ?
876 "BilinearForward" : "BilinearReverse",
877 "Unsolvable Matrix");
878 return((double *) NULL);
880 if ( *method == BilinearForwardDistortion ) {
881 /* Bilinear Forward Mapped Distortion
883 The above least-squares solved for coefficents but in the forward
884 direction, due to changes to indexing constants.
886 i = c0*x + c1*y + c2*x*y + c3;
887 j = c4*x + c5*y + c6*x*y + c7;
889 where u,v are in the destination image, NOT the source.
891 Reverse Pixel mapping however needs to use reverse of these
892 functions. It required a full page of algbra to work out the
893 reversed mapping formula, but resolves down to the following...
896 c9 = 2*(c2*c5-c1*c6); // '2*a' in the quadratic formula
898 i = i - c3; j = j - c7;
899 b = c6*i - c2*j + c8; // So that a*y^2 + b*y + c == 0
900 c = c4*i - c0*j; // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)
904 y = ( -b + sqrt(r) ) / c9;
908 x = ( i - c1*y) / ( c1 - c2*y );
910 NB: if 'r' is negative there is no solution!
911 NB: the sign of the sqrt() should be negative if image becomes
912 flipped or flopped, or crosses over itself.
913 NB: techniqually coefficient c5 is not needed, anymore,
914 but kept for completness.
916 See Anthony Thyssen <A.Thyssen@griffith.edu.au>
917 or Fred Weinhaus <fmw@alink.net> for more details.
920 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
921 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
926 case QuadrilateralDistortion:
928 /* Map a Quadrilateral to a unit square using BilinearReverse
929 Then map that unit square back to the final Quadrilateral
930 using BilinearForward.
932 Input Arguments are sets of control points...
933 For Distort Images u,v, x,y ...
934 For Sparse Gradients x,y, r,g,b ...
937 /* UNDER CONSTRUCTION */
942 case PolynomialDistortion:
944 /* Polynomial Distortion
946 First two coefficents are used to hole global polynomal information
947 c0 = Order of the polynimial being created
948 c1 = number_of_terms in one polynomial equation
950 Rest of the coefficients map to the equations....
951 v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
952 for each 'value' (number_values of them) given.
953 As such total coefficients = 2 + number_terms * number_values
955 Input Arguments are sets of control points...
956 For Distort Images order [u,v, x,y] ...
957 For Sparse Gradients order [x,y, r,g,b] ...
959 Polynomial Distortion Notes...
960 + UNDER DEVELOPMENT -- Do not expect this to remain as is.
961 + Currently polynomial is a reversed mapped distortion.
962 + Order 1.5 is fudged to map into a bilinear distortion.
963 though it is not the same order as that distortion.
971 nterms; /* number of polynomial terms per number_values */
979 /* first two coefficients hold polynomial order information */
980 coeff[0] = arguments[0];
981 coeff[1] = (double) poly_number_terms(arguments[0]);
982 nterms = (unsigned long) coeff[1];
984 /* create matrix, a fake vectors matrix, and least sqs terms */
985 matrix = AcquireMagickMatrix(nterms,nterms);
986 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
987 terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
988 if (matrix == (double **) NULL ||
989 vectors == (double **) NULL ||
990 terms == (double *) NULL )
992 matrix = RelinquishMagickMatrix(matrix, nterms);
993 vectors = (double **) RelinquishMagickMemory(vectors);
994 terms = (double *) RelinquishMagickMemory(terms);
995 coeff = (double *) RelinquishMagickMemory(coeff);
996 (void) ThrowMagickException(exception,GetMagickModule(),
997 ResourceLimitError,"MemoryAllocationFailed",
998 "%s", "DistortCoefficients");
999 return((double *) NULL);
1001 /* fake a number_values x3 vectors matrix from coefficients array */
1002 for (i=0; i < number_values; i++)
1003 vectors[i] = &(coeff[2+i*nterms]);
1004 /* Add given control point pairs for least squares solving */
1005 for (i=0; i < number_arguments; i+=cp_size) {
1006 for (j=0; j < (long) nterms; j++)
1007 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1008 LeastSquaresAddTerms(matrix,vectors,terms,
1009 &(arguments[i+cp_values]),nterms,number_values);
1011 terms = (double *) RelinquishMagickMemory(terms);
1012 /* Solve for LeastSquares Coefficients */
1013 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1014 matrix = RelinquishMagickMatrix(matrix, nterms);
1015 vectors = (double **) RelinquishMagickMemory(vectors);
1016 if ( status == MagickFalse ) {
1017 coeff = (double *) RelinquishMagickMemory(coeff);
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "InvalidArgument","%s : '%s'","Polynomial",
1020 "Unsolvable Matrix");
1021 return((double *) NULL);
1028 Args: arc_width rotate top_edge_radius bottom_edge_radius
1029 All but first argument are optional
1030 arc_width The angle over which to arc the image side-to-side
1031 rotate Angle to rotate image from vertical center
1032 top_radius Set top edge of source image at this radius
1033 bottom_radius Set bootom edge to this radius (radial scaling)
1035 By default, if the radii arguments are nor provided the image radius
1036 is calculated so the horizontal center-line is fits the given arc
1039 The output image size is ALWAYS adjusted to contain the whole image,
1040 and an offset is given to position image relative to the 0,0 point of
1041 the origin, allowing users to use relative positioning onto larger
1042 background (via -flatten).
1044 The arguments are converted to these coefficients
1045 c0: angle for center of source image
1046 c1: angle scale for mapping to source image
1047 c2: radius for top of source image
1048 c3: radius scale for mapping source image
1049 c4: centerline of arc within source image
1051 Note the coefficients use a center angle, so asymptotic join is
1052 furthest from both sides of the source image. This also means that
1053 for arc angles greater than 360 the sides of the image will be
1056 Arc Distortion Notes...
1057 + Does not use a set of CPs
1058 + Will only work with Image Distortion
1059 + Can not be used for generating a sparse gradient (interpolation)
1061 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1062 coeff = (double *) RelinquishMagickMemory(coeff);
1063 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1064 "InvalidArgument","%s : '%s'", "Arc",
1065 "Arc Angle Too Small");
1066 return((double *) NULL);
1068 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1069 coeff = (double *) RelinquishMagickMemory(coeff);
1070 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1071 "InvalidArgument","%s : '%s'", "Arc",
1072 "Outer Radius Too Small");
1073 return((double *) NULL);
1075 coeff[0] = -MagickPI2; /* -90, place at top! */
1076 if ( number_arguments >= 1 )
1077 coeff[1] = DegreesToRadians(arguments[0]);
1079 coeff[1] = MagickPI2; /* zero arguments - center is at top */
1080 if ( number_arguments >= 2 )
1081 coeff[0] += DegreesToRadians(arguments[1]);
1082 coeff[0] /= Magick2PI; /* normalize radians */
1083 coeff[0] -= MagickRound(coeff[0]);
1084 coeff[0] *= Magick2PI; /* de-normalize back to radians */
1085 coeff[3] = (double)image->rows-1;
1086 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1087 if ( number_arguments >= 3 ) {
1088 if ( number_arguments >= 4 )
1089 coeff[3] = arguments[2] - arguments[3];
1091 coeff[3] *= arguments[2]/coeff[2];
1092 coeff[2] = arguments[2];
1094 coeff[4] = ((double)image->columns-1.0)/2.0;
1098 case PolarDistortion:
1099 case DePolarDistortion:
1101 /* (De)Polar Distortion (same set of arguments)
1102 Args: Rmax, Rmin, Xcenter,Ycenter, Afrom,Ato
1103 DePolar can also have the extra arguments of Width, Height
1105 Coefficients 0 to 5 is the sanatized version first 6 input args
1106 Coefficient 6 is the angle to coord ratio and visa-versa
1107 Coefficient 7 is the radius to coord ratio and visa-versa
1109 WARNING: It is posible for Radius max<min and/or Angle from>to
1111 if ( number_arguments == 3
1112 || ( number_arguments > 6 && *method == PolarDistortion )
1113 || number_arguments > 8 ) {
1114 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1115 "InvalidArgument", "%s : number of arguments",
1116 *method == PolarDistortion ? "Polar" : "DePolar");
1117 coeff=(double *) RelinquishMagickMemory(coeff);
1118 return((double *) NULL);
1120 /* Rmax - if 0 calculate appropriate value */
1121 if ( number_arguments >= 1 )
1122 coeff[0] = arguments[0];
1125 /* Rmin - usally 0 */
1126 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1128 if ( number_arguments >= 4 ) {
1129 coeff[2] = arguments[2];
1130 coeff[3] = arguments[3];
1132 else { /* center of actual image */
1133 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1134 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1136 /* Angle from,to - about polar center 0 is downward */
1137 coeff[4] = -MagickPI;
1138 if ( number_arguments >= 5 )
1139 coeff[4] = DegreesToRadians(arguments[4]);
1140 coeff[5] = coeff[4];
1141 if ( number_arguments >= 6 )
1142 coeff[5] = DegreesToRadians(arguments[5]);
1143 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1144 coeff[5] += Magick2PI; /* same angle is a full circle */
1145 /* if radius 0 or negative, its a special value... */
1146 if ( coeff[0] < MagickEpsilon ) {
1147 /* Use closest edge if radius == 0 */
1148 if ( fabs(coeff[0]) < MagickEpsilon ) {
1149 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1150 fabs(coeff[3]-image->page.y));
1151 coeff[0]=MagickMin(coeff[0],
1152 fabs(coeff[2]-image->page.x-image->columns));
1153 coeff[0]=MagickMin(coeff[0],
1154 fabs(coeff[3]-image->page.y-image->rows));
1156 /* furthest diagonal if radius == -1 */
1157 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1159 rx = coeff[2]-image->page.x;
1160 ry = coeff[3]-image->page.y;
1161 coeff[0] = rx*rx+ry*ry;
1162 ry = coeff[3]-image->page.y-image->rows;
1163 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1164 rx = coeff[2]-image->page.x-image->columns;
1165 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1166 ry = coeff[3]-image->page.y;
1167 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1168 coeff[0] = sqrt(coeff[0]);
1171 /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
1172 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1173 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1174 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1175 "InvalidArgument", "%s : Invalid Radius",
1176 *method == PolarDistortion ? "Polar" : "DePolar");
1177 coeff=(double *) RelinquishMagickMemory(coeff);
1178 return((double *) NULL);
1180 /* converstion ratios */
1181 if ( *method == PolarDistortion ) {
1182 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1183 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1185 else { /* *method == DePolarDistortion */
1186 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1187 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1191 case BarrelDistortion:
1192 case BarrelInverseDistortion:
1194 /* Barrel Distortion
1195 Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
1196 BarrelInv Distortion
1197 Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
1199 Where Rd is the normalized radius from corner to middle of image
1200 Input Arguments are one of the following forms...
1205 Ax,Bx,Cx,Dx Ay,By,Cy,Dy
1206 Ax,Bx,Cx,Dx Ay,By,Cy,Dy X,Y
1208 Returns 10 coefficent values, which are de-normalized (pixel scale)
1209 Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, Xc, Yc
1211 /* Radius de-normalization scaling factor */
1213 rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
1215 if ( number_arguments != 4 && number_arguments != 6 &&
1216 number_arguments != 8 && number_arguments != 10 ) {
1217 coeff=(double *) RelinquishMagickMemory(coeff);
1218 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1219 "InvalidArgument", "%s : '%s'", "Barrel(Inv)",
1220 "number of arguments" );
1221 return((double *) NULL);
1223 /* A,B,C,D coefficients */
1224 coeff[0] = arguments[0];
1225 coeff[1] = arguments[1];
1226 coeff[2] = arguments[2];
1227 if ( number_arguments == 3 || number_arguments == 5 )
1228 coeff[3] = 1 - arguments[0] - arguments[1] - arguments[2];
1230 coeff[3] = arguments[3];
1231 /* de-normalize the X coefficients */
1232 coeff[0] *= pow(rscale,3.0);
1233 coeff[1] *= rscale*rscale;
1235 /* Y coefficients: as given OR as X coefficients */
1236 if ( number_arguments >= 8 ) {
1237 coeff[4] = arguments[4] * pow(rscale,3.0);
1238 coeff[5] = arguments[5] * rscale*rscale;
1239 coeff[6] = arguments[6] * rscale;
1240 coeff[7] = arguments[7];
1243 coeff[4] = coeff[0];
1244 coeff[5] = coeff[1];
1245 coeff[6] = coeff[2];
1246 coeff[7] = coeff[3];
1248 /* X,Y Center of Distortion */
1249 coeff[8] = ((double)image->columns-1)/2.0 + image->page.x;
1250 coeff[9] = ((double)image->rows-1)/2.0 + image->page.y;
1251 if ( number_arguments == 5 ) {
1252 coeff[8] = arguments[3];
1253 coeff[9] = arguments[4];
1255 if ( number_arguments == 6 ) {
1256 coeff[8] = arguments[4];
1257 coeff[9] = arguments[5];
1259 if ( number_arguments == 10 ) {
1260 coeff[8] = arguments[8];
1261 coeff[9] = arguments[9];
1265 case ShepardsDistortion:
1266 case VoronoiColorInterpolate:
1268 /* Shepards Distortion input arguments are the coefficents!
1269 Just check the number of arguments is valid!
1270 Args: u1,v1, x1,y1, ...
1271 OR : u1,v1, r1,g1,c1, ...
1273 if ( number_arguments%cp_size != 0 ||
1274 number_arguments < cp_size ) {
1275 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1276 "InvalidArgument", "%s : 'require at least %ld CPs'",
1278 coeff=(double *) RelinquishMagickMemory(coeff);
1279 return((double *) NULL);
1286 /* you should never reach this point */
1287 assert(! "No Method Handler"); /* just fail assertion */
1288 return((double *) NULL);
1292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296 % D i s t o r t I m a g e %
1300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302 % DistortImage() distorts an image using various distortion methods, by
1303 % mapping color lookups of the source image to a new destination image
1304 % usally of the same size as the source image, unless 'bestfit' is set to
1307 % If 'bestfit' is enabled, and distortion allows it, the destination image is
1308 % adjusted to ensure the whole source 'image' will just fit within the final
1309 % destination image, which will be sized and offset accordingly. Also in
1310 % many cases the virtual offset of the source image will be taken into
1311 % account in the mapping.
1313 % If the '-verbose' control option has been set print to standard error the
1314 % equicelent '-fx' formula with coefficients for the function, if practical.
1316 % The format of the DistortImage() method is:
1318 % Image *DistortImage(const Image *image,const DistortImageMethod method,
1319 % const unsigned long number_arguments,const double *arguments,
1320 % MagickBooleanType bestfit, ExceptionInfo *exception)
1322 % A description of each parameter follows:
1324 % o image: the image to be distorted.
1326 % o method: the method of image distortion.
1328 % ArcDistortion always ignores source image offset, and always
1329 % 'bestfit' the destination image with the top left corner offset
1330 % relative to the polar mapping center.
1332 % Affine, Perspective, and Bilinear, do least squares fitting of the
1333 % distrotion when more than the minimum number of control point pairs
1336 % Perspective, and Bilinear, fall back to a Affine distortion when less
1337 % than 4 control point pairs are provided. While Affine distortions
1338 % let you use any number of control point pairs, that is Zero pairs is
1339 % a No-Op (viewport only) distortion, one pair is a translation and
1340 % two pairs of control points do a scale-rotate-translate, without any
1343 % o number_arguments: the number of arguments given.
1345 % o arguments: an array of floating point arguments for this method.
1347 % o bestfit: Attempt to 'bestfit' the size of the resulting image.
1348 % This also forces the resulting image to be a 'layered' virtual
1349 % canvas image. Can be overridden using 'distort:viewport' setting.
1351 % o exception: return any errors or warnings in this structure
1353 % Extra Controls from Image meta-data (artifacts)...
1356 % Output to stderr alternatives, internal coefficents, and FX
1357 % equivelents for the distortion operation (if feasible).
1358 % This forms an extra check of the distortion method, and allows users
1359 % access to the internal constants IM calculates for the distortion.
1361 % o "distort:viewport"
1362 % Directly set the output image canvas area and offest to use for the
1363 % resulting image, rather than use the original images canvas, or a
1364 % calculated 'bestfit' canvas.
1367 % Scale the size of the output canvas by this amount to provide a
1368 % method of Zooming, and for super-sampling the results.
1370 % Other settings that can effect results include
1372 % o 'interpolate' For source image lookups (scale enlargements)
1374 % o 'filter' Set filter to use for area-resampling (scale shrinking).
1375 % Set to 'point' to turn off and use 'interpolate' lookup
1379 MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
1380 const unsigned long number_arguments,const double *arguments,
1381 MagickBooleanType bestfit,ExceptionInfo *exception)
1383 #define DistortImageTag "Distort/Image"
1393 geometry; /* geometry of the distorted space viewport */
1398 assert(image != (Image *) NULL);
1399 assert(image->signature == MagickSignature);
1400 if (image->debug != MagickFalse)
1401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1402 assert(exception != (ExceptionInfo *) NULL);
1403 assert(exception->signature == MagickSignature);
1406 Convert input arguments (usally as control points for reverse mapping)
1407 into mapping coefficients to apply the distortion.
1409 Note that some distortions are mapped to other distortions,
1410 and as such do not require specific code after this point.
1412 coeff = GenerateCoefficients(image, &method, number_arguments,
1413 arguments, 0, exception);
1414 if ( coeff == (double *) NULL )
1415 return((Image *) NULL);
1418 Determine the size and offset for a 'bestfit' destination.
1419 Usally the four corners of the source image is enough.
1422 /* default output image bounds, when no 'bestfit' is requested */
1423 geometry.width=image->columns;
1424 geometry.height=image->rows;
1428 if ( method == ArcDistortion ) {
1429 /* always use the 'best fit' viewport */
1430 bestfit = MagickTrue;
1433 /* Work out the 'best fit', (required for ArcDistortion) */
1438 s.x=s.y=min.x=max.x=min.y=max.y=0.0; /* keep compiler happy */
1440 /* defines to figure out the bounds of the distorted image */
1441 #define InitalBounds(p) \
1443 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1444 min.x = max.x = p.x; \
1445 min.y = max.y = p.y; \
1447 #define ExpandBounds(p) \
1449 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1450 min.x = MagickMin(min.x,p.x); \
1451 max.x = MagickMax(max.x,p.x); \
1452 min.y = MagickMin(min.y,p.y); \
1453 max.y = MagickMax(max.y,p.y); \
1458 case AffineDistortion:
1459 { double inverse[6];
1460 InvertAffineCoefficients(coeff, inverse);
1461 s.x = (double) image->page.x;
1462 s.y = (double) image->page.y;
1463 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1464 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1466 s.x = (double) image->page.x+image->columns;
1467 s.y = (double) image->page.y;
1468 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1469 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1471 s.x = (double) image->page.x;
1472 s.y = (double) image->page.y+image->rows;
1473 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1474 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1476 s.x = (double) image->page.x+image->columns;
1477 s.y = (double) image->page.y+image->rows;
1478 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1479 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1483 case PerspectiveDistortion:
1484 { double inverse[8], scale;
1485 InvertPerspectiveCoefficients(coeff, inverse);
1486 s.x = (double) image->page.x;
1487 s.y = (double) image->page.y;
1488 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1489 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1490 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1491 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1493 s.x = (double) image->page.x+image->columns;
1494 s.y = (double) image->page.y;
1495 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1496 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1497 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1498 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1500 s.x = (double) image->page.x;
1501 s.y = (double) image->page.y+image->rows;
1502 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1503 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1504 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1505 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1507 s.x = (double) image->page.x+image->columns;
1508 s.y = (double) image->page.y+image->rows;
1509 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1510 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1511 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1512 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1518 /* Forward Map Corners */
1519 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1523 d.x = (coeff[2]-coeff[3])*ca;
1524 d.y = (coeff[2]-coeff[3])*sa;
1526 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1530 d.x = (coeff[2]-coeff[3])*ca;
1531 d.y = (coeff[2]-coeff[3])*sa;
1533 /* Orthogonal points along top of arc */
1534 for( a=ceil((coeff[0]-coeff[1]/2.0)/MagickPI2)*MagickPI2;
1535 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1536 ca = cos(a); sa = sin(a);
1542 Convert the angle_to_width and radius_to_height
1543 to appropriate scaling factors, to allow faster processing
1544 in the mapping function.
1546 coeff[1] = Magick2PI*image->columns/coeff[1];
1547 coeff[3] = (double)image->rows/coeff[3];
1550 case PolarDistortion:
1552 if (number_arguments < 2)
1553 coeff[2] = coeff[3] = 0.0;
1554 min.x = coeff[2]-coeff[0];
1555 max.x = coeff[2]+coeff[0];
1556 min.y = coeff[3]-coeff[0];
1557 max.y = coeff[3]+coeff[0];
1558 /* should be about 1.0 if Rmin = 0 */
1559 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1562 case DePolarDistortion:
1564 /* direct calculation as it needs to tile correctly
1565 * for reversibility in a DePolar-Polar cycle */
1566 geometry.x = geometry.y = 0;
1567 geometry.height = (unsigned long) ceil(coeff[0]-coeff[1]);
1568 geometry.width = (unsigned long)
1569 ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
1572 case ShepardsDistortion:
1573 case BilinearForwardDistortion:
1574 case BilinearReverseDistortion:
1576 case QuadrilateralDistortion:
1578 case PolynomialDistortion:
1579 case BarrelDistortion:
1580 case BarrelInverseDistortion:
1582 /* no bestfit available for this distortion */
1583 bestfit = MagickFalse;
1586 /* Set the output image geometry to calculated 'bestfit'
1587 Do not do this for DePolar which needs to be exact for tiling
1589 if ( bestfit && method != DePolarDistortion ) {
1590 geometry.x = (long) floor(min.x-0.5);
1591 geometry.y = (long) floor(min.y-0.5);
1592 geometry.width=(unsigned long) ceil(max.x-geometry.x+0.5);
1593 geometry.height=(unsigned long) ceil(max.y-geometry.y+0.5);
1595 /* now that we have a new size lets fit distortion to it exactly */
1596 if ( method == DePolarDistortion ) {
1597 coeff[6]=(coeff[5]-coeff[4])/geometry.width; /* changed width */
1598 coeff[7]=(coeff[0]-coeff[1])/geometry.height; /* should be about 1.0 */
1602 /* The user provided a 'viewport' expert option which may
1603 overrides some parts of the current output image geometry.
1604 For ArcDistortion, this also overrides its default 'bestfit' setting.
1606 { const char *artifact=GetImageArtifact(image,"distort:viewport");
1607 viewport_given = MagickFalse;
1608 if ( artifact != (const char *) NULL ) {
1609 (void) ParseAbsoluteGeometry(artifact,&geometry);
1610 viewport_given = MagickTrue;
1614 /* Verbose output */
1615 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
1618 char image_gen[MaxTextExtent];
1621 /* Set destination image size and virtual offset */
1622 if ( bestfit || viewport_given ) {
1623 (void) FormatMagickString(image_gen, MaxTextExtent," -size %lux%lu "
1624 "-page %+ld%+ld xc: +insert \\\n",geometry.width,geometry.height,
1625 geometry.x,geometry.y);
1626 lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
1629 image_gen[0] = '\0'; /* no destination to generate */
1630 lookup = "p{ xx-page.x-.5, yy-page.x-.5 }"; /* simplify lookup */
1634 case AffineDistortion:
1638 inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
1639 if (inverse == (double *) NULL) {
1640 coeff = (double *) RelinquishMagickMemory(coeff);
1641 (void) ThrowMagickException(exception,GetMagickModule(),
1642 ResourceLimitError,"MemoryAllocationFailed",
1643 "%s", "DistortImages");
1644 return((Image *) NULL);
1646 InvertAffineCoefficients(coeff, inverse);
1647 CoefficientsToAffineArgs(inverse);
1648 fprintf(stderr, "Affine Projection:\n");
1649 fprintf(stderr, " -distort AffineProjection \\\n '");
1651 fprintf(stderr, "%lf,", inverse[i]);
1652 fprintf(stderr, "%lf'\n", inverse[5]);
1653 inverse = (double *) RelinquishMagickMemory(inverse);
1655 fprintf(stderr, "Affine Distort, FX Equivelent:\n");
1656 fprintf(stderr, "%s", image_gen);
1657 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1658 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
1659 coeff[0], coeff[1], coeff[2]);
1660 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
1661 coeff[3], coeff[4], coeff[5]);
1662 fprintf(stderr, " %s'\n", lookup);
1667 case PerspectiveDistortion:
1671 inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
1672 if (inverse == (double *) NULL) {
1673 coeff = (double *) RelinquishMagickMemory(coeff);
1674 (void) ThrowMagickException(exception,GetMagickModule(),
1675 ResourceLimitError,"MemoryAllocationFailed",
1676 "%s", "DistortCoefficients");
1677 return((Image *) NULL);
1679 InvertPerspectiveCoefficients(coeff, inverse);
1680 fprintf(stderr, "Perspective Projection:\n");
1681 fprintf(stderr, " -distort PerspectiveProjection \\\n '");
1683 fprintf(stderr, "%lf, ", inverse[i]);
1684 fprintf(stderr, "\n ");
1686 fprintf(stderr, "%lf, ", inverse[i]);
1687 fprintf(stderr, "%lf'\n", inverse[7]);
1688 inverse = (double *) RelinquishMagickMemory(inverse);
1690 fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
1691 fprintf(stderr, "%s", image_gen);
1692 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1693 fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
1694 coeff[6], coeff[7]);
1695 fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1696 coeff[0], coeff[1], coeff[2]);
1697 fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1698 coeff[3], coeff[4], coeff[5]);
1699 fprintf(stderr, " rr%s0 ? %s : blue'\n",
1700 coeff[8] < 0 ? "<" : ">", lookup);
1704 case BilinearForwardDistortion:
1705 fprintf(stderr, "BilinearForward Mapping Equations:\n");
1706 fprintf(stderr, "%s", image_gen);
1707 fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1708 coeff[0], coeff[1], coeff[2], coeff[3]);
1709 fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1710 coeff[4], coeff[5], coeff[6], coeff[7]);
1713 fprintf(stderr, " c8 = %+lf c9 = 2*a = %+lf;\n",
1714 coeff[8], coeff[9]);
1716 fprintf(stderr, "BilinearForward Distort, FX Equivelent:\n");
1717 fprintf(stderr, "%s", image_gen);
1718 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
1719 0.5-coeff[3], 0.5-coeff[7]);
1720 fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
1721 coeff[6], -coeff[2], coeff[8]);
1722 /* Handle Special degenerate (non-quadratic) or trapezoidal case */
1723 if ( coeff[9] != 0 ) {
1724 fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
1725 -2*coeff[9], coeff[4], -coeff[0]);
1726 fprintf(stderr, " yy=( -bb + sqrt(rt) ) / %lf;\n",
1729 fprintf(stderr, " yy=(%lf*ii%+lf*jj)/bb;\n",
1730 -coeff[4], coeff[0]);
1731 fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
1732 -coeff[1], coeff[0], coeff[2]);
1733 if ( coeff[9] != 0 )
1734 fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
1736 fprintf(stderr, " %s'\n", lookup);
1739 case BilinearReverseDistortion:
1741 fprintf(stderr, "Polynomial Projection Distort:\n");
1742 fprintf(stderr, " -distort PolynomialProjection \\\n");
1743 fprintf(stderr, " '1.5, %lf, %lf, %lf, %lf,\n",
1744 coeff[3], coeff[0], coeff[1], coeff[2]);
1745 fprintf(stderr, " %lf, %lf, %lf, %lf'\n",
1746 coeff[7], coeff[4], coeff[5], coeff[6]);
1748 fprintf(stderr, "BilinearReverse Distort, FX Equivelent:\n");
1749 fprintf(stderr, "%s", image_gen);
1750 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1751 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1752 coeff[0], coeff[1], coeff[2], coeff[3]);
1753 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1754 coeff[4], coeff[5], coeff[6], coeff[7]);
1755 fprintf(stderr, " %s'\n", lookup);
1758 case PolynomialDistortion:
1760 unsigned long nterms = (unsigned long) coeff[1];
1761 fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
1763 fprintf(stderr, "%s", image_gen);
1764 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1765 fprintf(stderr, " xx =");
1766 for (i=0; i<(long) nterms; i++) {
1767 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1768 fprintf(stderr, " %+lf%s", coeff[2+i],
1771 fprintf(stderr, ";\n yy =");
1772 for (i=0; i<(long) nterms; i++) {
1773 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1774 fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
1777 fprintf(stderr, ";\n %s'\n", lookup);
1782 fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
1783 for ( i=0; i<5; i++ )
1784 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
1785 fprintf(stderr, "Arc Distort, FX Equivelent:\n");
1786 fprintf(stderr, "%s", image_gen);
1787 fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
1788 fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
1790 fprintf(stderr, " xx=xx-round(xx);\n");
1791 fprintf(stderr, " xx=xx*%lf %+lf;\n",
1792 coeff[1], coeff[4]);
1793 fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
1794 coeff[2], coeff[3]);
1795 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1798 case PolarDistortion:
1800 fprintf(stderr, "Polar Distort, Internal Coefficents\n");
1801 for ( i=0; i<8; i++ )
1802 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
1803 fprintf(stderr, "Polar Distort, FX Equivelent:\n");
1804 fprintf(stderr, "%s", image_gen);
1805 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
1806 -coeff[2], -coeff[3]);
1807 fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
1808 -(coeff[4]+coeff[5])/2 );
1809 fprintf(stderr, " xx=xx-round(xx);\n");
1810 fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
1812 fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
1813 -coeff[1], coeff[7] );
1814 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1817 case DePolarDistortion:
1819 fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
1820 for ( i=0; i<8; i++ )
1821 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
1822 fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
1823 fprintf(stderr, "%s", image_gen);
1824 fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
1825 fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
1826 fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
1827 fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
1828 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1831 case BarrelDistortion:
1832 case BarrelInverseDistortion:
1834 xc = ((double)image->columns-1.0)/2.0 + image->page.x;
1835 yc = ((double)image->rows-1.0)/2.0 + image->page.y;
1836 fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
1837 method == BarrelDistortion ? "" : "Inv");
1838 fprintf(stderr, "%s", image_gen);
1839 if ( fabs(coeff[8]-xc) < 0.1 && fabs(coeff[9]-yc) < 0.1 )
1840 fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
1842 fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
1843 coeff[8], coeff[9]);
1845 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
1846 fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
1847 method == BarrelDistortion ? "*" : "/",
1848 coeff[0],coeff[1],coeff[2],coeff[3]);
1849 fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
1850 method == BarrelDistortion ? "*" : "/",
1851 coeff[4],coeff[5],coeff[6],coeff[7]);
1852 fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
1859 /* The user provided a 'scale' expert option will scale the
1860 output image size, by the factor given allowing for super-sampling
1861 of the distorted image space. Any scaling factors must naturally
1862 be halved as a result.
1864 { const char *artifact;
1865 artifact=GetImageArtifact(image,"distort:scale");
1866 output_scaling = 1.0;
1867 if (artifact != (const char *) NULL) {
1868 output_scaling = fabs(atof(artifact));
1869 geometry.width *= output_scaling;
1870 geometry.height *= output_scaling;
1871 geometry.x *= output_scaling;
1872 geometry.y *= output_scaling;
1873 if ( output_scaling < 0.1 ) {
1874 coeff = (double *) RelinquishMagickMemory(coeff);
1875 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1876 "InvalidArgument","%s", "-set option:distort:scale" );
1877 return((Image *) NULL);
1879 output_scaling = 1/output_scaling;
1882 #define ScaleFilter(F,A,B,C,D) \
1883 ScaleResampleFilter( (F), \
1884 output_scaling*(A), output_scaling*(B), \
1885 output_scaling*(C), output_scaling*(D) )
1888 Initialize the distort image attributes.
1890 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
1892 if (distort_image == (Image *) NULL)
1893 return((Image *) NULL);
1894 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
1895 { /* if image is ColorMapped - change it to DirectClass */
1896 InheritException(exception,&distort_image->exception);
1897 distort_image=DestroyImage(distort_image);
1898 return((Image *) NULL);
1900 distort_image->page.x=geometry.x;
1901 distort_image->page.y=geometry.y;
1902 if (distort_image->background_color.opacity != OpaqueOpacity)
1903 distort_image->matte=MagickTrue;
1905 { /* ----- MAIN CODE -----
1906 Sample the source image to each pixel in the distort image.
1924 GetMagickPixelPacket(distort_image,&zero);
1925 resample_filter=AcquireResampleFilterThreadSet(image,MagickFalse,exception);
1926 distort_view=AcquireCacheView(distort_image);
1927 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
1928 #pragma omp parallel for shared(progress,status)
1930 for (j=0; j < (long) distort_image->rows; j++)
1933 validity; /* how mathematically valid is this the mapping */
1939 pixel, /* pixel color to assign to distorted image */
1940 invalid; /* the color to assign when distort result is invalid */
1943 d,s; /* transform destination image x,y to source image x,y */
1945 register IndexPacket
1946 *__restrict indexes;
1952 register PixelPacket
1955 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
1957 if (q == (PixelPacket *) NULL)
1962 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
1965 /* Define constant scaling vectors for Affine Distortions
1966 Other methods are either variable, or use interpolated lookup
1968 id=GetOpenMPThreadId();
1971 case AffineDistortion:
1972 ScaleFilter( resample_filter[id],
1974 coeff[3], coeff[4] );
1980 /* Initialize default pixel validity
1981 * negative: pixel is invalid output 'matte_color'
1982 * 0.0 to 1.0: antialiased, mix with resample output
1983 * 1.0 or greater: use resampled output.
1987 GetMagickPixelPacket(distort_image,&invalid);
1988 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
1989 (IndexPacket *) NULL, &invalid);
1990 if (distort_image->colorspace == CMYKColorspace)
1991 ConvertRGBToCMYK(&invalid); /* what about other color spaces? */
1993 for (i=0; i < (long) distort_image->columns; i++)
1995 /* map pixel coordinate to distortion space coordinate */
1996 d.x = (double) (geometry.x+i+0.5)*output_scaling;
1997 d.y = (double) (geometry.y+j+0.5)*output_scaling;
1998 s = d; /* default is a no-op mapping */
2001 case AffineDistortion:
2003 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2004 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2005 /* Affine partial derivitives are constant -- set above */
2008 case PerspectiveDistortion:
2011 p,q,r,abs_r,abs_c6,abs_c7,scale;
2012 /* perspective is a ratio of affines */
2013 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2014 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2015 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2016 /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
2017 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2018 /* Determine horizon anti-alias blending */
2020 abs_c6 = fabs(coeff[6]);
2021 abs_c7 = fabs(coeff[7]);
2022 if ( abs_c6 > abs_c7 ) {
2023 if ( abs_r < abs_c6 )
2024 validity = 0.5 - coeff[8]*r/coeff[6];
2026 else if ( abs_r < abs_c7 )
2027 validity = 0.5 - coeff[8]*r/coeff[7];
2028 /* Perspective Sampling Point (if valid) */
2029 if ( validity > 0.0 ) {
2030 /* divide by r affine, for perspective scaling */
2034 /* Perspective Partial Derivatives or Scaling Vectors */
2036 ScaleFilter( resample_filter[id],
2037 (r*coeff[0] - p*coeff[6])*scale,
2038 (r*coeff[1] - p*coeff[7])*scale,
2039 (r*coeff[3] - q*coeff[6])*scale,
2040 (r*coeff[4] - q*coeff[7])*scale );
2044 case BilinearReverseDistortion:
2046 /* Reversed Mapped is just a simple polynomial */
2047 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2048 s.y=coeff[4]*d.x+coeff[5]*d.y
2049 +coeff[6]*d.x*d.y+coeff[7];
2050 /* Bilinear partial derivitives of scaling vectors */
2051 ScaleFilter( resample_filter[id],
2052 coeff[0] + coeff[2]*d.y,
2053 coeff[1] + coeff[2]*d.x,
2054 coeff[4] + coeff[6]*d.y,
2055 coeff[5] + coeff[6]*d.x );
2058 case BilinearForwardDistortion:
2060 /* Forward mapped needs reversed polynomial equations
2061 * which unfortunatally requires a square root! */
2063 d.x -= coeff[3]; d.y -= coeff[7];
2064 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2065 c = coeff[4]*d.x - coeff[0]*d.y;
2068 /* Handle Special degenerate (non-quadratic) case */
2069 if ( fabs(coeff[9]) < MagickEpsilon )
2072 c = b*b - 2*coeff[9]*c;
2076 s.y = ( -b + sqrt(c) )/coeff[9];
2078 if ( validity > 0.0 )
2079 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2081 /* NOTE: the sign of the square root should be -ve for parts
2082 where the source image becomes 'flipped' or 'mirrored'.
2083 FUTURE: Horizon handling
2084 FUTURE: Scaling factors or Deritives (how?)
2089 case QuadrilateralDistortion:
2090 /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
2091 /* UNDER DEVELOPMENT */
2094 case PolynomialDistortion:
2096 /* multi-ordered polynomial */
2100 nterms=(long)coeff[1];
2103 du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
2105 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2106 for(k=0; k < nterms; k++) {
2107 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2108 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2109 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2110 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2111 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2112 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2114 ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
2119 /* what is the angle and radius in the destination image */
2120 s.x = (atan2(d.y,d.x) - coeff[0])/Magick2PI;
2121 s.x -= MagickRound(s.x); /* angle */
2122 s.y = hypot(d.x,d.y); /* radius */
2124 /* Arc Distortion Partial Scaling Vectors
2125 Are derived by mapping the perpendicular unit vectors
2126 dR and dA*R*2PI rather than trying to map dx and dy
2127 The results is a very simple orthogonal aligned ellipse.
2129 if ( s.y > MagickEpsilon )
2130 ScaleFilter( resample_filter[id],
2131 coeff[1]/(Magick2PI*s.y), 0, 0, coeff[3] );
2133 ScaleFilter( resample_filter[id],
2134 distort_image->columns*2, 0, 0, coeff[3] );
2136 /* now scale the angle and radius for source image lookup point */
2137 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2138 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2141 case PolarDistortion:
2142 { /* Rect/Cartesain/Cylinder to Polar View */
2145 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2147 s.x -= MagickRound(s.x);
2148 s.x *= Magick2PI; /* angle - relative to centerline */
2149 s.y = hypot(d.x,d.y); /* radius */
2151 /* Polar Scaling vectors are based on mapping dR and dA vectors
2152 This results in very simple orthogonal scaling vectors
2154 if ( s.y > MagickEpsilon )
2155 ScaleFilter( resample_filter[id],
2156 coeff[6]/(Magick2PI*s.y), 0, 0, coeff[7] );
2158 ScaleFilter( resample_filter[id],
2159 distort_image->columns*2, 0, 0, coeff[7] );
2161 /* now finish mapping radius/angle to source x,y coords */
2162 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2163 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2166 case DePolarDistortion:
2167 { /* Polar to Cylindical */
2168 /* ignore all destination virtual offsets */
2169 d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
2170 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2171 s.x = d.y*sin(d.x) + coeff[2];
2172 s.y = d.y*cos(d.x) + coeff[3];
2173 /* derivatives are usless - better to use SuperSampling */
2176 case BarrelDistortion:
2177 case BarrelInverseDistortion:
2179 double r,fx,fy,gx,gy;
2180 /* Radial Polynomial Distortion (de-normalized) */
2183 r = sqrt(d.x*d.x+d.y*d.y);
2184 if ( r > MagickEpsilon ) {
2185 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2186 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2187 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2188 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2189 /* adjust functions and scaling for 'inverse' form */
2190 if ( method == BarrelInverseDistortion ) {
2191 fx = 1/fx; fy = 1/fy;
2192 gx *= -fx*fx; gy *= -fy*fy;
2194 /* Set source pixel and EWA derivative vectors */
2195 s.x = d.x*fx + coeff[8];
2196 s.y = d.y*fy + coeff[9];
2197 ScaleFilter( resample_filter[id],
2198 gx*d.x*d.x + fx, gx*d.x*d.y,
2199 gy*d.x*d.y, gy*d.y*d.y + fy );
2201 else { /* Special handling to avoid divide by zero when r=0 */
2203 if ( method == BarrelDistortion )
2204 ScaleFilter( resample_filter[id],
2205 coeff[3], 0, 0, coeff[7] );
2206 else /* method == BarrelInverseDistortion */
2207 /* FUTURE, trap for D==0 causing division by zero */
2208 ScaleFilter( resample_filter[id],
2209 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2213 case ShepardsDistortion:
2214 { /* Shepards Method, or Inverse Weighted Distance for
2215 displacement around the destination image control points
2216 The input arguments are the coefficents to the function.
2217 This is more of a 'displacement' function rather than an
2218 absolute distortion function.
2225 denominator = s.x = s.y = 0;
2226 for(i=0; i<number_arguments; i+=4) {
2228 ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
2229 + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
2235 s.x += (arguments[ i ]-arguments[i+2])*weight;
2236 s.y += (arguments[i+1]-arguments[i+3])*weight;
2237 denominator += weight;
2244 /* We can not determine derivatives using shepards method
2245 only color interpolatation, not area-resampling */
2249 break; /* use the default no-op given above */
2251 /* map virtual canvas location back to real image coordinate */
2252 if ( bestfit && method != ArcDistortion ) {
2253 s.x -= image->page.x;
2254 s.y -= image->page.y;
2259 if ( validity <= 0.0 ) {
2260 /* result of distortion is an invalid pixel - don't resample */
2261 SetPixelPacket(distort_image,&invalid,q,indexes);
2264 /* resample the source image to find its correct color */
2265 (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
2266 /* if validity between 0.0 and 1.0 mix result with invalid pixel */
2267 if ( validity < 1.0 ) {
2268 /* Do a blend of sample color and invalid pixel */
2269 /* should this be a 'Blend', or an 'Over' compose */
2270 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
2273 SetPixelPacket(distort_image,&pixel,q,indexes);
2278 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2279 if (sync == MagickFalse)
2281 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2286 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
2287 #pragma omp critical (MagickCore_DistortImage)
2289 proceed=SetImageProgress(image,DistortImageTag,progress++,
2291 if (proceed == MagickFalse)
2295 fprintf(stderr, "\n");
2298 distort_view=DestroyCacheView(distort_view);
2299 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
2301 if (status == MagickFalse)
2302 distort_image=DestroyImage(distort_image);
2305 /* Arc does not return an offset unless 'bestfit' is in effect
2306 And the user has not provided an overriding 'viewport'.
2308 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2309 distort_image->page.x = 0;
2310 distort_image->page.y = 0;
2312 coeff = (double *) RelinquishMagickMemory(coeff);
2313 return(distort_image);
2317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2321 % S p a r s e C o l o r I m a g e %
2325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2327 % SparseColorImage(), given a set of coordinates, interpolates the colors
2328 % found at those coordinates, across the whole image, using various methods.
2330 % The format of the SparseColorImage() method is:
2332 % Image *SparseColorImage(const Image *image,const ChannelType channel,
2333 % SparseColorMethod method,const unsigned long number_arguments,
2334 % const double *arguments,ExceptionInfo *exception)
2336 % A description of each parameter follows:
2338 % o image: the image to be filled in.
2340 % o channel: Specify which color values (in RGBKA sequence) are being set.
2341 % This also determines the number of color_values in above.
2343 % o method: the method to fill in the gradient between the control points.
2345 % The methods used for SparseColor() are often simular to methods
2346 % used for DistortImage(), and even share the same code for determination
2347 % of the function coefficents, though with more dimensions (or resulting
2350 % o number_arguments: the number of arguments given.
2352 % o arguments: array of floating point arguments for this method--
2353 % x,y,color_values-- with color_values given as normalized values.
2355 % o exception: return any errors or warnings in this structure
2358 MagickExport Image *SparseColorImage(const Image *image,
2359 const ChannelType channel,SparseColorMethod method,
2360 const unsigned long number_arguments,const double *arguments,
2361 ExceptionInfo *exception)
2363 #define SparseColorTag "Distort/SparseColor"
2380 assert(image != (Image *) NULL);
2381 assert(image->signature == MagickSignature);
2382 if (image->debug != MagickFalse)
2383 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2384 assert(exception != (ExceptionInfo *) NULL);
2385 assert(exception->signature == MagickSignature);
2387 /* Determine number of color values needed per control point */
2389 if ( channel & RedChannel ) number_colors++;
2390 if ( channel & GreenChannel ) number_colors++;
2391 if ( channel & BlueChannel ) number_colors++;
2392 if ( channel & IndexChannel ) number_colors++;
2393 if ( channel & OpacityChannel ) number_colors++;
2396 Convert input arguments into mapping coefficients to apply the distortion.
2397 Note some Methods may fall back to other simpler methods.
2399 distort_method=(DistortImageMethod *) &method;
2400 coeff = GenerateCoefficients(image, distort_method, number_arguments,
2401 arguments, number_colors, exception);
2402 if ( coeff == (double *) NULL )
2403 return((Image *) NULL);
2405 /* Verbose output */
2406 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
2409 case BarycentricColorInterpolate:
2412 fprintf(stderr, "Barycentric Sparse Color:\n");
2413 if ( channel & RedChannel )
2414 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
2415 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2416 if ( channel & GreenChannel )
2417 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
2418 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2419 if ( channel & BlueChannel )
2420 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
2421 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2422 if ( channel & IndexChannel )
2423 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
2424 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2425 if ( channel & OpacityChannel )
2426 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
2427 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2430 case BilinearColorInterpolate:
2433 fprintf(stderr, "Bilinear Sparse Color\n");
2434 if ( channel & RedChannel )
2435 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2436 coeff[ x ], coeff[x+1],
2437 coeff[x+2], coeff[x+3]),x+=4;
2438 if ( channel & GreenChannel )
2439 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2440 coeff[ x ], coeff[x+1],
2441 coeff[x+2], coeff[x+3]),x+=4;
2442 if ( channel & BlueChannel )
2443 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2444 coeff[ x ], coeff[x+1],
2445 coeff[x+2], coeff[x+3]),x+=4;
2446 if ( channel & IndexChannel )
2447 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2448 coeff[ x ], coeff[x+1],
2449 coeff[x+2], coeff[x+3]),x+=4;
2450 if ( channel & OpacityChannel )
2451 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2452 coeff[ x ], coeff[x+1],
2453 coeff[x+2], coeff[x+3]),x+=4;
2457 /* unknown, or which are too complex for FX alturnatives */
2462 /* Generate new image for generated interpolated gradient.
2463 * ASIDE: Actually we could have just replaced the colors of the original
2464 * image, but IM core policy, is if storage class could change then clone
2468 sparse_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2470 if (sparse_image == (Image *) NULL)
2471 return((Image *) NULL);
2472 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
2473 { /* if image is ColorMapped - change it to DirectClass */
2474 InheritException(exception,&image->exception);
2475 sparse_image=DestroyImage(sparse_image);
2476 return((Image *) NULL);
2478 { /* ----- MAIN CODE ----- */
2491 GetMagickPixelPacket(sparse_image,&zero);
2492 sparse_view=AcquireCacheView(sparse_image);
2493 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
2494 #pragma omp parallel for shared(progress,status)
2496 for (j=0; j < (long) sparse_image->rows; j++)
2502 pixel; /* pixel to assign to distorted image */
2504 register IndexPacket
2505 *__restrict indexes;
2510 register PixelPacket
2513 q=QueueCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
2515 if (q == (PixelPacket *) NULL)
2520 /* FUTURE: get pixel from source image - so channel can replace parts */
2521 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
2523 for (i=0; i < (long) sparse_image->columns; i++)
2527 case BarycentricColorInterpolate:
2530 if ( channel & RedChannel )
2531 pixel.red = coeff[x]*i +coeff[x+1]*j
2533 if ( channel & GreenChannel )
2534 pixel.green = coeff[x]*i +coeff[x+1]*j
2536 if ( channel & BlueChannel )
2537 pixel.blue = coeff[x]*i +coeff[x+1]*j
2539 if ( channel & IndexChannel )
2540 pixel.index = coeff[x]*i +coeff[x+1]*j
2542 if ( channel & OpacityChannel )
2543 pixel.opacity = coeff[x]*i +coeff[x+1]*j
2547 case BilinearColorInterpolate:
2550 if ( channel & RedChannel )
2551 pixel.red = coeff[x]*i + coeff[x+1]*j +
2552 coeff[x+2]*i*j + coeff[x+3], x+=4;
2553 if ( channel & GreenChannel )
2554 pixel.green = coeff[x]*i + coeff[x+1]*j +
2555 coeff[x+2]*i*j + coeff[x+3], x+=4;
2556 if ( channel & BlueChannel )
2557 pixel.blue = coeff[x]*i + coeff[x+1]*j +
2558 coeff[x+2]*i*j + coeff[x+3], x+=4;
2559 if ( channel & IndexChannel )
2560 pixel.index = coeff[x]*i + coeff[x+1]*j +
2561 coeff[x+2]*i*j + coeff[x+3], x+=4;
2562 if ( channel & OpacityChannel )
2563 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
2564 coeff[x+2]*i*j + coeff[x+3], x+=4;
2567 case ShepardsColorInterpolate:
2568 { /* Shepards Method,uses its own input arguments as coefficients.
2575 if ( channel & RedChannel ) pixel.red = 0.0;
2576 if ( channel & GreenChannel ) pixel.green = 0.0;
2577 if ( channel & BlueChannel ) pixel.blue = 0.0;
2578 if ( channel & IndexChannel ) pixel.index = 0.0;
2579 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
2581 for(k=0; k<number_arguments; k+=2+number_colors) {
2582 register long x=(long) k+2;
2584 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2585 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2590 if ( channel & RedChannel )
2591 pixel.red += arguments[x++]*weight;
2592 if ( channel & GreenChannel )
2593 pixel.green += arguments[x++]*weight;
2594 if ( channel & BlueChannel )
2595 pixel.blue += arguments[x++]*weight;
2596 if ( channel & IndexChannel )
2597 pixel.index += arguments[x++]*weight;
2598 if ( channel & OpacityChannel )
2599 pixel.opacity += arguments[x++]*weight;
2600 denominator += weight;
2602 if ( channel & RedChannel ) pixel.red /= denominator;
2603 if ( channel & GreenChannel ) pixel.green /= denominator;
2604 if ( channel & BlueChannel ) pixel.blue /= denominator;
2605 if ( channel & IndexChannel ) pixel.index /= denominator;
2606 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
2609 case VoronoiColorInterpolate:
2611 { /* Just use the closest control point you can find! */
2615 minimum = MagickHuge;
2617 for(k=0; k<number_arguments; k+=2+number_colors) {
2619 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2620 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2621 if ( distance < minimum ) {
2622 register long x=(long) k+2;
2623 if ( channel & RedChannel ) pixel.red = arguments[x++];
2624 if ( channel & GreenChannel ) pixel.green = arguments[x++];
2625 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
2626 if ( channel & IndexChannel ) pixel.index = arguments[x++];
2627 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
2634 /* set the color directly back into the source image */
2635 if ( channel & RedChannel ) pixel.red *= QuantumRange;
2636 if ( channel & GreenChannel ) pixel.green *= QuantumRange;
2637 if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
2638 if ( channel & IndexChannel ) pixel.index *= QuantumRange;
2639 if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
2640 SetPixelPacket(sparse_image,&pixel,q,indexes);
2644 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
2645 if (sync == MagickFalse)
2647 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2652 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
2653 #pragma omp critical (MagickCore_SparseColorImage)
2655 proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
2656 if (proceed == MagickFalse)
2660 sparse_view=DestroyCacheView(sparse_view);
2661 if (status == MagickFalse)
2662 sparse_image=DestroyImage(sparse_image);
2664 coeff = (double *) RelinquishMagickMemory(coeff);
2665 return(sparse_image);