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-2011 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/option.h"
60 #include "magick/pixel.h"
61 #include "magick/pixel-private.h"
62 #include "magick/resample.h"
63 #include "magick/resample-private.h"
64 #include "magick/registry.h"
65 #include "magick/semaphore.h"
66 #include "magick/string_.h"
67 #include "magick/string-private.h"
68 #include "magick/thread-private.h"
69 #include "magick/token.h"
70 #include "magick/transform.h"
73 Numerous internal routines for image distortions.
75 static inline double MagickMin(const double x,const double y)
77 return( x < y ? x : y);
79 static inline double MagickMax(const double x,const double y)
81 return( x > y ? x : y);
84 static inline void AffineArgsToCoefficients(double *affine)
86 /* map external sx,ry,rx,sy,tx,ty to internal c0,c2,c4,c1,c3,c5 */
87 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
88 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
89 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
92 static inline void CoefficientsToAffineArgs(double *coeff)
94 /* map internal c0,c1,c2,c3,c4,c5 to external sx,ry,rx,sy,tx,ty */
95 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
96 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
97 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
99 static void InvertAffineCoefficients(const double *coeff,double *inverse)
101 /* From "Digital Image Warping" by George Wolberg, page 50 */
104 determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
105 inverse[0]=determinant*coeff[4];
106 inverse[1]=determinant*(-coeff[1]);
107 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
108 inverse[3]=determinant*(-coeff[3]);
109 inverse[4]=determinant*coeff[0];
110 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
113 static void InvertPerspectiveCoefficients(const double *coeff,
116 /* From "Digital Image Warping" by George Wolberg, page 53 */
119 determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
120 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
121 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
122 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
123 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
124 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
125 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
126 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
127 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
130 static inline double MagickRound(double x)
133 Round the fraction to nearest integer.
136 return((double) ((ssize_t) (x+0.5)));
137 return((double) ((ssize_t) (x-0.5)));
141 * Polynomial Term Defining Functions
143 * Order must either be an integer, or 1.5 to produce
144 * the 2 number_valuesal polynomial function...
145 * affine 1 (3) u = c0 + c1*x + c2*y
146 * bilinear 1.5 (4) u = '' + c3*x*y
147 * quadratic 2 (6) u = '' + c4*x*x + c5*y*y
148 * cubic 3 (10) u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
149 * quartic 4 (15) u = '' + c10*x^4 + ... + c14*y^4
150 * quintic 5 (21) u = '' + c15*x^5 + ... + c20*y^5
151 * number in parenthesis minimum number of points needed.
152 * Anything beyond quintic, has not been implemented until
153 * a more automated way of determining terms is found.
155 * Note the slight re-ordering of the terms for a quadratic polynomial
156 * which is to allow the use of a bi-linear (order=1.5) polynomial.
157 * All the later polynomials are ordered simply from x^N to y^N
159 static size_t poly_number_terms(double order)
161 /* Return the number of terms for a 2d polynomial */
162 if ( order < 1 || order > 5 ||
163 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
164 return 0; /* invalid polynomial order */
165 return((size_t) floor((order+1)*(order+2)/2));
168 static double poly_basis_fn(ssize_t n, double x, double y)
170 /* Return the result for this polynomial term */
172 case 0: return( 1.0 ); /* constant */
174 case 2: return( y ); /* affine order = 1 terms = 3 */
175 case 3: return( x*y ); /* bilinear order = 1.5 terms = 4 */
176 case 4: return( x*x );
177 case 5: return( y*y ); /* quadratic order = 2 terms = 6 */
178 case 6: return( x*x*x );
179 case 7: return( x*x*y );
180 case 8: return( x*y*y );
181 case 9: return( y*y*y ); /* cubic order = 3 terms = 10 */
182 case 10: return( x*x*x*x );
183 case 11: return( x*x*x*y );
184 case 12: return( x*x*y*y );
185 case 13: return( x*y*y*y );
186 case 14: return( y*y*y*y ); /* quartic order = 4 terms = 15 */
187 case 15: return( x*x*x*x*x );
188 case 16: return( x*x*x*x*y );
189 case 17: return( x*x*x*y*y );
190 case 18: return( x*x*y*y*y );
191 case 19: return( x*y*y*y*y );
192 case 20: return( y*y*y*y*y ); /* quintic order = 5 terms = 21 */
194 return( 0 ); /* should never happen */
196 static const char *poly_basis_str(ssize_t n)
198 /* return the result for this polynomial term */
200 case 0: return(""); /* constant */
201 case 1: return("*ii");
202 case 2: return("*jj"); /* affine order = 1 terms = 3 */
203 case 3: return("*ii*jj"); /* bilinear order = 1.5 terms = 4 */
204 case 4: return("*ii*ii");
205 case 5: return("*jj*jj"); /* quadratic order = 2 terms = 6 */
206 case 6: return("*ii*ii*ii");
207 case 7: return("*ii*ii*jj");
208 case 8: return("*ii*jj*jj");
209 case 9: return("*jj*jj*jj"); /* cubic order = 3 terms = 10 */
210 case 10: return("*ii*ii*ii*ii");
211 case 11: return("*ii*ii*ii*jj");
212 case 12: return("*ii*ii*jj*jj");
213 case 13: return("*ii*jj*jj*jj");
214 case 14: return("*jj*jj*jj*jj"); /* quartic order = 4 terms = 15 */
215 case 15: return("*ii*ii*ii*ii*ii");
216 case 16: return("*ii*ii*ii*ii*jj");
217 case 17: return("*ii*ii*ii*jj*jj");
218 case 18: return("*ii*ii*jj*jj*jj");
219 case 19: return("*ii*jj*jj*jj*jj");
220 case 20: return("*jj*jj*jj*jj*jj"); /* quintic order = 5 terms = 21 */
222 return( "UNKNOWN" ); /* should never happen */
224 static double poly_basis_dx(ssize_t n, double x, double y)
226 /* polynomial term for x derivative */
228 case 0: return( 0.0 ); /* constant */
229 case 1: return( 1.0 );
230 case 2: return( 0.0 ); /* affine order = 1 terms = 3 */
231 case 3: return( y ); /* bilinear order = 1.5 terms = 4 */
233 case 5: return( 0.0 ); /* quadratic order = 2 terms = 6 */
234 case 6: return( x*x );
235 case 7: return( x*y );
236 case 8: return( y*y );
237 case 9: return( 0.0 ); /* cubic order = 3 terms = 10 */
238 case 10: return( x*x*x );
239 case 11: return( x*x*y );
240 case 12: return( x*y*y );
241 case 13: return( y*y*y );
242 case 14: return( 0.0 ); /* quartic order = 4 terms = 15 */
243 case 15: return( x*x*x*x );
244 case 16: return( x*x*x*y );
245 case 17: return( x*x*y*y );
246 case 18: return( x*y*y*y );
247 case 19: return( y*y*y*y );
248 case 20: return( 0.0 ); /* quintic order = 5 terms = 21 */
250 return( 0.0 ); /* should never happen */
252 static double poly_basis_dy(ssize_t n, double x, double y)
254 /* polynomial term for y derivative */
256 case 0: return( 0.0 ); /* constant */
257 case 1: return( 0.0 );
258 case 2: return( 1.0 ); /* affine order = 1 terms = 3 */
259 case 3: return( x ); /* bilinear order = 1.5 terms = 4 */
260 case 4: return( 0.0 );
261 case 5: return( y ); /* quadratic order = 2 terms = 6 */
262 default: return( poly_basis_dx(n-1,x,y) ); /* weird but true */
264 /* NOTE: the only reason that last is not true for 'quadratic'
265 is due to the re-arrangement of terms to allow for 'bilinear'
270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274 + G e n e r a t e C o e f f i c i e n t s %
278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280 % GenerateCoefficients() takes user provided input arguments and generates
281 % the coefficients, needed to apply the specific distortion for either
282 % distorting images (generally using control points) or generating a color
283 % gradient from sparsely separated color points.
285 % The format of the GenerateCoefficients() method is:
287 % Image *GenerateCoefficients(const Image *image,DistortImageMethod method,
288 % const size_t number_arguments,const double *arguments,
289 % size_t number_values, ExceptionInfo *exception)
291 % A description of each parameter follows:
293 % o image: the image to be distorted.
295 % o method: the method of image distortion/ sparse gradient
297 % o number_arguments: the number of arguments given.
299 % o arguments: the arguments for this distortion method.
301 % o number_values: the style and format of given control points, (caller type)
302 % 0: 2 dimensional mapping of control points (Distort)
303 % Format: u,v,x,y where u,v is the 'source' of the
304 % the color to be plotted, for DistortImage()
305 % N: Interpolation of control points with N values (usally r,g,b)
306 % Format: x,y,r,g,b mapping x,y to color values r,g,b
307 % IN future, variable number of values may be given (1 to N)
309 % o exception: return any errors or warnings in this structure
311 % Note that the returned array of double values must be freed by the
312 % calling method using RelinquishMagickMemory(). This however may change in
313 % the future to require a more 'method' specific method.
315 % Because of this this method should not be classed as stable or used
316 % outside other MagickCore library methods.
319 static double *GenerateCoefficients(const Image *image,
320 DistortImageMethod *method,const size_t number_arguments,
321 const double *arguments,size_t number_values,ExceptionInfo *exception)
330 number_coeff, /* number of coefficients to return (array size) */
331 cp_size, /* number floating point numbers per control point */
332 cp_x,cp_y, /* the x,y indexes for control point */
333 cp_values; /* index of values for this control point */
334 /* number_values Number of values given per control point */
336 if ( number_values == 0 ) {
337 /* Image distortion using control points (or other distortion)
338 That is generate a mapping so that x,y->u,v given u,v,x,y
340 number_values = 2; /* special case: two values of u,v */
341 cp_values = 0; /* the values i,j are BEFORE the destination CP x,y */
342 cp_x = 2; /* location of x,y in input control values */
344 /* NOTE: cp_values, also used for later 'reverse map distort' tests */
347 cp_x = 0; /* location of x,y in input control values */
349 cp_values = 2; /* and the other values are after x,y */
350 /* Typically in this case the values are R,G,B color values */
352 cp_size = number_values+2; /* each CP defintion involves this many numbers */
354 /* If not enough control point pairs are found for specific distortions
355 fall back to Affine distortion (allowing 0 to 3 point pairs)
357 if ( number_arguments < 4*cp_size &&
358 ( *method == BilinearForwardDistortion
359 || *method == BilinearReverseDistortion
360 || *method == PerspectiveDistortion
362 *method = AffineDistortion;
366 case AffineDistortion:
367 /* also BarycentricColorInterpolate: */
368 number_coeff=3*number_values;
370 case PolynomialDistortion:
371 /* number of coefficents depend on the given polynomal 'order' */
372 if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
374 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
375 "InvalidArgument","%s : '%s'","Polynomial",
376 "Invalid number of args: order [CPs]...");
377 return((double *) NULL);
379 i = poly_number_terms(arguments[0]);
380 number_coeff = 2 + i*number_values;
382 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
383 "InvalidArgument","%s : '%s'","Polynomial",
384 "Invalid order, should be interger 1 to 5, or 1.5");
385 return((double *) NULL);
387 if ( number_arguments < 1+i*cp_size ) {
388 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
389 "InvalidArgument", "%s : 'require at least %.20g CPs'",
390 "Polynomial", (double) i);
391 return((double *) NULL);
394 case BilinearReverseDistortion:
395 number_coeff=4*number_values;
398 The rest are constants as they are only used for image distorts
400 case BilinearForwardDistortion:
401 number_coeff=10; /* 2*4 coeff plus 2 constants */
402 cp_x = 0; /* Reverse src/dest coords for forward mapping */
407 case QuadraterialDistortion:
408 number_coeff=19; /* BilinearForward + BilinearReverse */
411 case ShepardsDistortion:
412 case VoronoiColorInterpolate:
413 number_coeff=1; /* not used, but provide some type of return */
418 case ScaleRotateTranslateDistortion:
419 case AffineProjectionDistortion:
422 case PolarDistortion:
423 case DePolarDistortion:
426 case PerspectiveDistortion:
427 case PerspectiveProjectionDistortion:
430 case BarrelDistortion:
431 case BarrelInverseDistortion:
435 assert(! "Unknown Method Given"); /* just fail assertion */
438 /* allocate the array of coefficients needed */
439 coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
440 if (coeff == (double *) NULL) {
441 (void) ThrowMagickException(exception,GetMagickModule(),
442 ResourceLimitError,"MemoryAllocationFailed",
443 "%s", "GenerateCoefficients");
444 return((double *) NULL);
447 /* zero out coefficients array */
448 for (i=0; i < number_coeff; i++)
453 case AffineDistortion:
457 for each 'value' given
459 Input Arguments are sets of control points...
460 For Distort Images u,v, x,y ...
461 For Sparse Gradients x,y, r,g,b ...
463 if ( number_arguments%cp_size != 0 ||
464 number_arguments < cp_size ) {
465 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
466 "InvalidArgument", "%s : 'require at least %.20g CPs'",
468 coeff=(double *) RelinquishMagickMemory(coeff);
469 return((double *) NULL);
471 /* handle special cases of not enough arguments */
472 if ( number_arguments == cp_size ) {
473 /* Only 1 CP Set Given */
474 if ( cp_values == 0 ) {
475 /* image distortion - translate the image */
477 coeff[2] = arguments[0] - arguments[2];
479 coeff[5] = arguments[1] - arguments[3];
482 /* sparse gradient - use the values directly */
483 for (i=0; i<number_values; i++)
484 coeff[i*3+2] = arguments[cp_values+i];
488 /* 2 or more points (usally 3) given.
489 Solve a least squares simultaneous equation for coefficients.
499 /* create matrix, and a fake vectors matrix */
500 matrix = AcquireMagickMatrix(3UL,3UL);
501 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
502 if (matrix == (double **) NULL || vectors == (double **) NULL)
504 matrix = RelinquishMagickMatrix(matrix, 3UL);
505 vectors = (double **) RelinquishMagickMemory(vectors);
506 coeff = (double *) RelinquishMagickMemory(coeff);
507 (void) ThrowMagickException(exception,GetMagickModule(),
508 ResourceLimitError,"MemoryAllocationFailed",
509 "%s", "DistortCoefficients");
510 return((double *) NULL);
512 /* fake a number_values x3 vectors matrix from coefficients array */
513 for (i=0; i < number_values; i++)
514 vectors[i] = &(coeff[i*3]);
515 /* Add given control point pairs for least squares solving */
516 for (i=0; i < number_arguments; i+=cp_size) {
517 terms[0] = arguments[i+cp_x]; /* x */
518 terms[1] = arguments[i+cp_y]; /* y */
519 terms[2] = 1; /* 1 */
520 LeastSquaresAddTerms(matrix,vectors,terms,
521 &(arguments[i+cp_values]),3UL,number_values);
523 if ( number_arguments == 2*cp_size ) {
524 /* Only two pairs were given, but we need 3 to solve the affine.
525 Fake extra coordinates by rotating p1 around p0 by 90 degrees.
526 x2 = x0 - (y1-y0) y2 = y0 + (x1-x0)
528 terms[0] = arguments[cp_x]
529 - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
530 terms[1] = arguments[cp_y] +
531 + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
532 terms[2] = 1; /* 1 */
533 if ( cp_values == 0 ) {
534 /* Image Distortion - rotate the u,v coordients too */
537 uv2[0] = arguments[0] - arguments[5] + arguments[1]; /* u2 */
538 uv2[1] = arguments[1] + arguments[4] - arguments[0]; /* v2 */
539 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
542 /* Sparse Gradient - use values of p0 for linear gradient */
543 LeastSquaresAddTerms(matrix,vectors,terms,
544 &(arguments[cp_values]),3UL,number_values);
547 /* Solve for LeastSquares Coefficients */
548 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
549 matrix = RelinquishMagickMatrix(matrix, 3UL);
550 vectors = (double **) RelinquishMagickMemory(vectors);
551 if ( status == MagickFalse ) {
552 coeff = (double *) RelinquishMagickMemory(coeff);
553 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
554 "InvalidArgument","%s : 'Unsolvable Matrix'",
555 CommandOptionToMnemonic(MagickDistortOptions, *method) );
556 return((double *) NULL);
561 case AffineProjectionDistortion:
564 Arguments: Affine Matrix (forward mapping)
565 Arguments sx, rx, ry, sy, tx, ty
566 Where u = sx*x + ry*y + tx
569 Returns coefficients (in there inverse form) ordered as...
572 AffineProjection Distortion Notes...
573 + Will only work with a 2 number_values for Image Distortion
574 + Can not be used for generating a sparse gradient (interpolation)
577 if (number_arguments != 6) {
578 coeff = (double *) RelinquishMagickMemory(coeff);
579 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
580 "InvalidArgument","%s : 'Needs 6 coeff values'",
581 CommandOptionToMnemonic(MagickDistortOptions, *method) );
582 return((double *) NULL);
584 /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinant = 0, no inverse) */
585 for(i=0; i<6UL; i++ )
586 inverse[i] = arguments[i];
587 AffineArgsToCoefficients(inverse); /* map into coefficents */
588 InvertAffineCoefficients(inverse, coeff); /* invert */
589 *method = AffineDistortion;
593 case ScaleRotateTranslateDistortion:
595 /* Scale, Rotate and Translate Distortion
596 An alternative Affine Distortion
597 Argument options, by number of arguments given:
598 7: x,y, sx,sy, a, nx,ny
605 Where actions are (in order of application)
606 x,y 'center' of transforms (default = image center)
607 sx,sy scale image by this amount (default = 1)
608 a angle of rotation (argument required)
609 nx,ny move 'center' here (default = x,y or no movement)
610 And convert to affine mapping coefficients
612 ScaleRotateTranslate Distortion Notes...
613 + Does not use a set of CPs in any normal way
614 + Will only work with a 2 number_valuesal Image Distortion
615 + Cannot be used for generating a sparse gradient (interpolation)
621 /* set default center, and default scale */
622 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
623 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
625 switch ( number_arguments ) {
627 coeff = (double *) RelinquishMagickMemory(coeff);
628 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
629 "InvalidArgument","%s : 'Needs at least 1 argument'",
630 CommandOptionToMnemonic(MagickDistortOptions, *method) );
631 return((double *) NULL);
636 sx = sy = arguments[0];
640 x = nx = arguments[0];
641 y = ny = arguments[1];
642 switch ( number_arguments ) {
647 sx = sy = arguments[2];
656 sx = sy = arguments[2];
669 coeff = (double *) RelinquishMagickMemory(coeff);
670 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
671 "InvalidArgument","%s : 'Too Many Arguments (7 or less)'",
672 CommandOptionToMnemonic(MagickDistortOptions, *method) );
673 return((double *) NULL);
677 /* Trap if sx or sy == 0 -- image is scaled out of existance! */
678 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
679 coeff = (double *) RelinquishMagickMemory(coeff);
680 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
681 "InvalidArgument","%s : 'Zero Scale Given'",
682 CommandOptionToMnemonic(MagickDistortOptions, *method) );
683 return((double *) NULL);
685 /* Save the given arguments as an affine distortion */
686 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
688 *method = AffineDistortion;
691 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
694 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
697 case PerspectiveDistortion:
699 Perspective Distortion (a ratio of affine distortions)
701 p(x,y) c0*x + c1*y + c2
702 u = ------ = ------------------
703 r(x,y) c6*x + c7*y + 1
705 q(x,y) c3*x + c4*y + c5
706 v = ------ = ------------------
707 r(x,y) c6*x + c7*y + 1
709 c8 = Sign of 'r', or the denominator affine, for the actual image.
710 This determines what part of the distorted image is 'ground'
711 side of the horizon, the other part is 'sky' or invalid.
712 Valid values are +1.0 or -1.0 only.
714 Input Arguments are sets of control points...
715 For Distort Images u,v, x,y ...
716 For Sparse Gradients x,y, r,g,b ...
718 Perspective Distortion Notes...
719 + Can be thought of as ratio of 3 affine transformations
720 + Not separatable: r() or c6 and c7 are used by both equations
721 + All 8 coefficients must be determined simultaniously
722 + Will only work with a 2 number_valuesal Image Distortion
723 + Can not be used for generating a sparse gradient (interpolation)
724 + It is not linear, but is simple to generate an inverse
725 + All lines within an image remain lines.
726 + but distances between points may vary.
740 if ( number_arguments%cp_size != 0 ||
741 number_arguments < cp_size*4 ) {
742 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
743 "InvalidArgument", "%s : 'require at least %.20g CPs'",
744 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
745 coeff=(double *) RelinquishMagickMemory(coeff);
746 return((double *) NULL);
748 /* fake 1x8 vectors matrix directly using the coefficients array */
749 vectors[0] = &(coeff[0]);
750 /* 8x8 least-squares matrix (zeroed) */
751 matrix = AcquireMagickMatrix(8UL,8UL);
752 if (matrix == (double **) NULL) {
753 (void) ThrowMagickException(exception,GetMagickModule(),
754 ResourceLimitError,"MemoryAllocationFailed",
755 "%s", "DistortCoefficients");
756 return((double *) NULL);
758 /* Add control points for least squares solving */
759 for (i=0; i < number_arguments; i+=4) {
760 terms[0]=arguments[i+cp_x]; /* c0*x */
761 terms[1]=arguments[i+cp_y]; /* c1*y */
762 terms[2]=1.0; /* c2*1 */
766 terms[6]=-terms[0]*arguments[i+cp_u]; /* 1/(c6*x) */
767 terms[7]=-terms[1]*arguments[i+cp_u]; /* 1/(c7*y) */
768 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
774 terms[3]=arguments[i+cp_x]; /* c3*x */
775 terms[4]=arguments[i+cp_y]; /* c4*y */
776 terms[5]=1.0; /* c5*1 */
777 terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
778 terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
779 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
782 /* Solve for LeastSquares Coefficients */
783 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
784 matrix = RelinquishMagickMatrix(matrix, 8UL);
785 if ( status == MagickFalse ) {
786 coeff = (double *) RelinquishMagickMemory(coeff);
787 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
788 "InvalidArgument","%s : 'Unsolvable Matrix'",
789 CommandOptionToMnemonic(MagickDistortOptions, *method) );
790 return((double *) NULL);
793 Calculate 9'th coefficient! The ground-sky determination.
794 What is sign of the 'ground' in r() denominator affine function?
795 Just use any valid image coordinate (first control point) in
796 destination for determination of what part of view is 'ground'.
798 coeff[8] = coeff[6]*arguments[cp_x]
799 + coeff[7]*arguments[cp_y] + 1.0;
800 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
804 case PerspectiveProjectionDistortion:
807 Arguments: Perspective Coefficents (forward mapping)
809 if (number_arguments != 8) {
810 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
811 "InvalidArgument", "%s : 'Needs 8 coefficient values'",
812 CommandOptionToMnemonic(MagickDistortOptions, *method));
813 return((double *) NULL);
815 /* FUTURE: trap test c0*c4-c3*c1 == 0 (determinate = 0, no inverse) */
816 InvertPerspectiveCoefficients(arguments, coeff);
818 Calculate 9'th coefficient! The ground-sky determination.
819 What is sign of the 'ground' in r() denominator affine function?
820 Just use any valid image cocodinate in destination for determination.
821 For a forward mapped perspective the images 0,0 coord will map to
822 c2,c5 in the distorted image, so set the sign of denominator of that.
824 coeff[8] = coeff[6]*arguments[2]
825 + coeff[7]*arguments[5] + 1.0;
826 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
827 *method = PerspectiveDistortion;
831 case BilinearForwardDistortion:
832 case BilinearReverseDistortion:
834 /* Bilinear Distortion (Forward mapping)
835 v = c0*x + c1*y + c2*x*y + c3;
836 for each 'value' given
838 This is actually a simple polynomial Distortion! The difference
839 however is when we need to reverse the above equation to generate a
840 BilinearForwardDistortion (see below).
842 Input Arguments are sets of control points...
843 For Distort Images u,v, x,y ...
844 For Sparse Gradients x,y, r,g,b ...
855 /* check the number of arguments */
856 if ( number_arguments%cp_size != 0 ||
857 number_arguments < cp_size*4 ) {
858 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
859 "InvalidArgument", "%s : 'require at least %.20g CPs'",
860 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
861 coeff=(double *) RelinquishMagickMemory(coeff);
862 return((double *) NULL);
864 /* create matrix, and a fake vectors matrix */
865 matrix = AcquireMagickMatrix(4UL,4UL);
866 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
867 if (matrix == (double **) NULL || vectors == (double **) NULL)
869 matrix = RelinquishMagickMatrix(matrix, 4UL);
870 vectors = (double **) RelinquishMagickMemory(vectors);
871 coeff = (double *) RelinquishMagickMemory(coeff);
872 (void) ThrowMagickException(exception,GetMagickModule(),
873 ResourceLimitError,"MemoryAllocationFailed",
874 "%s", "DistortCoefficients");
875 return((double *) NULL);
877 /* fake a number_values x4 vectors matrix from coefficients array */
878 for (i=0; i < number_values; i++)
879 vectors[i] = &(coeff[i*4]);
880 /* Add given control point pairs for least squares solving */
881 for (i=0; i < number_arguments; i+=cp_size) {
882 terms[0] = arguments[i+cp_x]; /* x */
883 terms[1] = arguments[i+cp_y]; /* y */
884 terms[2] = terms[0]*terms[1]; /* x*y */
885 terms[3] = 1; /* 1 */
886 LeastSquaresAddTerms(matrix,vectors,terms,
887 &(arguments[i+cp_values]),4UL,number_values);
889 /* Solve for LeastSquares Coefficients */
890 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
891 matrix = RelinquishMagickMatrix(matrix, 4UL);
892 vectors = (double **) RelinquishMagickMemory(vectors);
893 if ( status == MagickFalse ) {
894 coeff = (double *) RelinquishMagickMemory(coeff);
895 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
896 "InvalidArgument","%s : 'Unsolvable Matrix'",
897 CommandOptionToMnemonic(MagickDistortOptions, *method) );
898 return((double *) NULL);
900 if ( *method == BilinearForwardDistortion ) {
901 /* Bilinear Forward Mapped Distortion
903 The above least-squares solved for coefficents but in the forward
904 direction, due to changes to indexing constants.
906 i = c0*x + c1*y + c2*x*y + c3;
907 j = c4*x + c5*y + c6*x*y + c7;
909 where i,j are in the destination image, NOT the source.
911 Reverse Pixel mapping however needs to use reverse of these
912 functions. It required a full page of algbra to work out the
913 reversed mapping formula, but resolves down to the following...
916 c9 = 2*(c2*c5-c1*c6); // '2*a' in the quadratic formula
918 i = i - c3; j = j - c7;
919 b = c6*i - c2*j + c8; // So that a*y^2 + b*y + c == 0
920 c = c4*i - c0*j; // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)
924 y = ( -b + sqrt(r) ) / c9;
928 x = ( i - c1*y) / ( c1 - c2*y );
930 NB: if 'r' is negative there is no solution!
931 NB: the sign of the sqrt() should be negative if image becomes
932 flipped or flopped, or crosses over itself.
933 NB: techniqually coefficient c5 is not needed, anymore,
934 but kept for completness.
936 See Anthony Thyssen <A.Thyssen@griffith.edu.au>
937 or Fred Weinhaus <fmw@alink.net> for more details.
940 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
941 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
946 case QuadrilateralDistortion:
948 /* Map a Quadrilateral to a unit square using BilinearReverse
949 Then map that unit square back to the final Quadrilateral
950 using BilinearForward.
952 Input Arguments are sets of control points...
953 For Distort Images u,v, x,y ...
954 For Sparse Gradients x,y, r,g,b ...
957 /* UNDER CONSTRUCTION */
962 case PolynomialDistortion:
964 /* Polynomial Distortion
966 First two coefficents are used to hole global polynomal information
967 c0 = Order of the polynimial being created
968 c1 = number_of_terms in one polynomial equation
970 Rest of the coefficients map to the equations....
971 v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
972 for each 'value' (number_values of them) given.
973 As such total coefficients = 2 + number_terms * number_values
975 Input Arguments are sets of control points...
976 For Distort Images order [u,v, x,y] ...
977 For Sparse Gradients order [x,y, r,g,b] ...
979 Polynomial Distortion Notes...
980 + UNDER DEVELOPMENT -- Do not expect this to remain as is.
981 + Currently polynomial is a reversed mapped distortion.
982 + Order 1.5 is fudged to map into a bilinear distortion.
983 though it is not the same order as that distortion.
991 nterms; /* number of polynomial terms per number_values */
999 /* first two coefficients hold polynomial order information */
1000 coeff[0] = arguments[0];
1001 coeff[1] = (double) poly_number_terms(arguments[0]);
1002 nterms = (size_t) coeff[1];
1004 /* create matrix, a fake vectors matrix, and least sqs terms */
1005 matrix = AcquireMagickMatrix(nterms,nterms);
1006 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
1007 terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
1008 if (matrix == (double **) NULL ||
1009 vectors == (double **) NULL ||
1010 terms == (double *) NULL )
1012 matrix = RelinquishMagickMatrix(matrix, nterms);
1013 vectors = (double **) RelinquishMagickMemory(vectors);
1014 terms = (double *) RelinquishMagickMemory(terms);
1015 coeff = (double *) RelinquishMagickMemory(coeff);
1016 (void) ThrowMagickException(exception,GetMagickModule(),
1017 ResourceLimitError,"MemoryAllocationFailed",
1018 "%s", "DistortCoefficients");
1019 return((double *) NULL);
1021 /* fake a number_values x3 vectors matrix from coefficients array */
1022 for (i=0; i < number_values; i++)
1023 vectors[i] = &(coeff[2+i*nterms]);
1024 /* Add given control point pairs for least squares solving */
1025 for (i=1; i < number_arguments; i+=cp_size) { /* NB: start = 1 not 0 */
1026 for (j=0; j < (ssize_t) nterms; j++)
1027 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1028 LeastSquaresAddTerms(matrix,vectors,terms,
1029 &(arguments[i+cp_values]),nterms,number_values);
1031 terms = (double *) RelinquishMagickMemory(terms);
1032 /* Solve for LeastSquares Coefficients */
1033 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1034 matrix = RelinquishMagickMatrix(matrix, nterms);
1035 vectors = (double **) RelinquishMagickMemory(vectors);
1036 if ( status == MagickFalse ) {
1037 coeff = (double *) RelinquishMagickMemory(coeff);
1038 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1039 "InvalidArgument","%s : 'Unsolvable Matrix'",
1040 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1041 return((double *) NULL);
1048 Args: arc_width rotate top_edge_radius bottom_edge_radius
1049 All but first argument are optional
1050 arc_width The angle over which to arc the image side-to-side
1051 rotate Angle to rotate image from vertical center
1052 top_radius Set top edge of source image at this radius
1053 bottom_radius Set bootom edge to this radius (radial scaling)
1055 By default, if the radii arguments are nor provided the image radius
1056 is calculated so the horizontal center-line is fits the given arc
1059 The output image size is ALWAYS adjusted to contain the whole image,
1060 and an offset is given to position image relative to the 0,0 point of
1061 the origin, allowing users to use relative positioning onto larger
1062 background (via -flatten).
1064 The arguments are converted to these coefficients
1065 c0: angle for center of source image
1066 c1: angle scale for mapping to source image
1067 c2: radius for top of source image
1068 c3: radius scale for mapping source image
1069 c4: centerline of arc within source image
1071 Note the coefficients use a center angle, so asymptotic join is
1072 furthest from both sides of the source image. This also means that
1073 for arc angles greater than 360 the sides of the image will be
1076 Arc Distortion Notes...
1077 + Does not use a set of CPs
1078 + Will only work with Image Distortion
1079 + Can not be used for generating a sparse gradient (interpolation)
1081 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1082 coeff = (double *) RelinquishMagickMemory(coeff);
1083 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1084 "InvalidArgument","%s : 'Arc Angle Too Small'",
1085 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1086 return((double *) NULL);
1088 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1089 coeff = (double *) RelinquishMagickMemory(coeff);
1090 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1091 "InvalidArgument","%s : 'Outer Radius Too Small'",
1092 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1093 return((double *) NULL);
1095 coeff[0] = -MagickPI2; /* -90, place at top! */
1096 if ( number_arguments >= 1 )
1097 coeff[1] = DegreesToRadians(arguments[0]);
1099 coeff[1] = MagickPI2; /* zero arguments - center is at top */
1100 if ( number_arguments >= 2 )
1101 coeff[0] += DegreesToRadians(arguments[1]);
1102 coeff[0] /= Magick2PI; /* normalize radians */
1103 coeff[0] -= MagickRound(coeff[0]);
1104 coeff[0] *= Magick2PI; /* de-normalize back to radians */
1105 coeff[3] = (double)image->rows-1;
1106 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1107 if ( number_arguments >= 3 ) {
1108 if ( number_arguments >= 4 )
1109 coeff[3] = arguments[2] - arguments[3];
1111 coeff[3] *= arguments[2]/coeff[2];
1112 coeff[2] = arguments[2];
1114 coeff[4] = ((double)image->columns-1.0)/2.0;
1118 case PolarDistortion:
1119 case DePolarDistortion:
1121 /* (De)Polar Distortion (same set of arguments)
1122 Args: Rmax, Rmin, Xcenter,Ycenter, Afrom,Ato
1123 DePolar can also have the extra arguments of Width, Height
1125 Coefficients 0 to 5 is the sanatized version first 6 input args
1126 Coefficient 6 is the angle to coord ratio and visa-versa
1127 Coefficient 7 is the radius to coord ratio and visa-versa
1129 WARNING: It is posible for Radius max<min and/or Angle from>to
1131 if ( number_arguments == 3
1132 || ( number_arguments > 6 && *method == PolarDistortion )
1133 || number_arguments > 8 ) {
1134 (void) ThrowMagickException(exception,GetMagickModule(),
1135 OptionError,"InvalidArgument", "%s : number of arguments",
1136 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1137 coeff=(double *) RelinquishMagickMemory(coeff);
1138 return((double *) NULL);
1140 /* Rmax - if 0 calculate appropriate value */
1141 if ( number_arguments >= 1 )
1142 coeff[0] = arguments[0];
1145 /* Rmin - usally 0 */
1146 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1148 if ( number_arguments >= 4 ) {
1149 coeff[2] = arguments[2];
1150 coeff[3] = arguments[3];
1152 else { /* center of actual image */
1153 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1154 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1156 /* Angle from,to - about polar center 0 is downward */
1157 coeff[4] = -MagickPI;
1158 if ( number_arguments >= 5 )
1159 coeff[4] = DegreesToRadians(arguments[4]);
1160 coeff[5] = coeff[4];
1161 if ( number_arguments >= 6 )
1162 coeff[5] = DegreesToRadians(arguments[5]);
1163 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1164 coeff[5] += Magick2PI; /* same angle is a full circle */
1165 /* if radius 0 or negative, its a special value... */
1166 if ( coeff[0] < MagickEpsilon ) {
1167 /* Use closest edge if radius == 0 */
1168 if ( fabs(coeff[0]) < MagickEpsilon ) {
1169 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1170 fabs(coeff[3]-image->page.y));
1171 coeff[0]=MagickMin(coeff[0],
1172 fabs(coeff[2]-image->page.x-image->columns));
1173 coeff[0]=MagickMin(coeff[0],
1174 fabs(coeff[3]-image->page.y-image->rows));
1176 /* furthest diagonal if radius == -1 */
1177 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1179 rx = coeff[2]-image->page.x;
1180 ry = coeff[3]-image->page.y;
1181 coeff[0] = rx*rx+ry*ry;
1182 ry = coeff[3]-image->page.y-image->rows;
1183 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1184 rx = coeff[2]-image->page.x-image->columns;
1185 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1186 ry = coeff[3]-image->page.y;
1187 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1188 coeff[0] = sqrt(coeff[0]);
1191 /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
1192 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1193 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1194 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1195 "InvalidArgument", "%s : Invalid Radius",
1196 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1197 coeff=(double *) RelinquishMagickMemory(coeff);
1198 return((double *) NULL);
1200 /* converstion ratios */
1201 if ( *method == PolarDistortion ) {
1202 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1203 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1205 else { /* *method == DePolarDistortion */
1206 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1207 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1211 case BarrelDistortion:
1212 case BarrelInverseDistortion:
1214 /* Barrel Distortion
1215 Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
1216 BarrelInv Distortion
1217 Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
1219 Where Rd is the normalized radius from corner to middle of image
1220 Input Arguments are one of the following forms (number of arguments)...
1225 8: Ax,Bx,Cx,Dx Ay,By,Cy,Dy
1226 10: Ax,Bx,Cx,Dx Ay,By,Cy,Dy X,Y
1228 Returns 10 coefficent values, which are de-normalized (pixel scale)
1229 Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, Xc, Yc
1231 /* Radius de-normalization scaling factor */
1233 rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
1235 /* sanity check number of args must = 3,4,5,6,8,10 or error */
1236 if ( (number_arguments < 3) || (number_arguments == 7) ||
1237 (number_arguments == 9) || (number_arguments > 10) )
1239 coeff=(double *) RelinquishMagickMemory(coeff);
1240 (void) ThrowMagickException(exception,GetMagickModule(),
1241 OptionError,"InvalidArgument", "%s : number of arguments",
1242 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1243 return((double *) NULL);
1245 /* A,B,C,D coefficients */
1246 coeff[0] = arguments[0];
1247 coeff[1] = arguments[1];
1248 coeff[2] = arguments[2];
1249 if ((number_arguments == 3) || (number_arguments == 5) )
1250 coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1252 coeff[3] = arguments[3];
1253 /* de-normalize the coefficients */
1254 coeff[0] *= pow(rscale,3.0);
1255 coeff[1] *= rscale*rscale;
1257 /* Y coefficients: as given OR same as X coefficients */
1258 if ( number_arguments >= 8 ) {
1259 coeff[4] = arguments[4] * pow(rscale,3.0);
1260 coeff[5] = arguments[5] * rscale*rscale;
1261 coeff[6] = arguments[6] * rscale;
1262 coeff[7] = arguments[7];
1265 coeff[4] = coeff[0];
1266 coeff[5] = coeff[1];
1267 coeff[6] = coeff[2];
1268 coeff[7] = coeff[3];
1270 /* X,Y Center of Distortion (image coodinates) */
1271 if ( number_arguments == 5 ) {
1272 coeff[8] = arguments[3];
1273 coeff[9] = arguments[4];
1275 else if ( number_arguments == 6 ) {
1276 coeff[8] = arguments[4];
1277 coeff[9] = arguments[5];
1279 else if ( number_arguments == 10 ) {
1280 coeff[8] = arguments[8];
1281 coeff[9] = arguments[9];
1284 /* center of the image provided (image coodinates) */
1285 coeff[8] = (double)image->columns/2.0 + image->page.x;
1286 coeff[9] = (double)image->rows/2.0 + image->page.y;
1290 case ShepardsDistortion:
1291 case VoronoiColorInterpolate:
1293 /* Shepards Distortion input arguments are the coefficents!
1294 Just check the number of arguments is valid!
1295 Args: u1,v1, x1,y1, ...
1296 OR : u1,v1, r1,g1,c1, ...
1298 if ( number_arguments%cp_size != 0 ||
1299 number_arguments < cp_size ) {
1300 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1301 "InvalidArgument", "%s : 'require at least %.20g CPs'",
1302 CommandOptionToMnemonic(MagickDistortOptions, *method), 1.0);
1303 coeff=(double *) RelinquishMagickMemory(coeff);
1304 return((double *) NULL);
1311 /* you should never reach this point */
1312 assert(! "No Method Handler"); /* just fail assertion */
1313 return((double *) NULL);
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321 + D i s t o r t R e s i z e I m a g e %
1325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1327 % DistortResizeImage() resize image using the equivelent but slower image
1328 % distortion operator. The filter is applied using a EWA cylindrical
1329 % resampling. But like resize the final image size is limited to whole pixels
1330 % with no effects by virtual-pixels on the result.
1332 % Note that images containing a transparency channel will be twice as slow to
1333 % resize as images one without transparency.
1335 % The format of the DistortResizeImage method is:
1337 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1338 % const size_t rows,ExceptionInfo *exception)
1340 % A description of each parameter follows:
1342 % o image: the image.
1344 % o columns: the number of columns in the resized image.
1346 % o rows: the number of rows in the resized image.
1348 % o exception: return any errors or warnings in this structure.
1351 MagickExport Image *DistortResizeImage(const Image *image,
1352 const size_t columns,const size_t rows,ExceptionInfo *exception)
1354 #define DistortResizeImageTag "Distort/Image"
1370 Distort resize image.
1372 assert(image != (const Image *) NULL);
1373 assert(image->signature == MagickSignature);
1374 if (image->debug != MagickFalse)
1375 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1376 assert(exception != (ExceptionInfo *) NULL);
1377 assert(exception->signature == MagickSignature);
1378 if ((columns == 0) || (rows == 0))
1379 return((Image *) NULL);
1380 /* Do not short-circuit this resize if final image size is unchanged */
1382 (void) SetImageVirtualPixelMethod(image,TransparentVirtualPixelMethod);
1384 (void) ResetMagickMemory(distort_args,0,12*sizeof(double));
1385 distort_args[4]=(double) image->columns;
1386 distort_args[6]=(double) columns;
1387 distort_args[9]=(double) image->rows;
1388 distort_args[11]=(double) rows;
1390 vp_save=GetImageVirtualPixelMethod(image);
1392 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1393 if ( tmp_image == (Image *) NULL )
1394 return((Image *) NULL);
1395 (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod);
1397 if (image->matte == MagickFalse)
1400 Image has not transparency channel, so we free to use it
1402 (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel);
1403 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1404 MagickTrue,exception),
1406 tmp_image=DestroyImage(tmp_image);
1407 if ( resize_image == (Image *) NULL )
1408 return((Image *) NULL);
1410 (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
1411 InheritException(exception,&image->exception);
1416 Image has transparency so handle colors and alpha separatly.
1417 Basically we need to separate Virtual-Pixel alpha in the resized
1418 image, so only the actual original images alpha channel is used.
1423 /* distort alpha channel separatally */
1424 (void) SeparateImageChannel(tmp_image,TrueAlphaChannel);
1425 (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel);
1426 resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1427 MagickTrue,exception),
1428 tmp_image=DestroyImage(tmp_image);
1429 if ( resize_alpha == (Image *) NULL )
1430 return((Image *) NULL);
1432 /* distort the actual image containing alpha + VP alpha */
1433 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1434 if ( tmp_image == (Image *) NULL )
1435 return((Image *) NULL);
1436 (void) SetImageVirtualPixelMethod(tmp_image,
1437 TransparentVirtualPixelMethod);
1438 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1439 MagickTrue,exception),
1440 tmp_image=DestroyImage(tmp_image);
1441 if ( resize_image == (Image *) NULL)
1443 resize_alpha=DestroyImage(resize_alpha);
1444 return((Image *) NULL);
1447 /* replace resize images alpha with the separally distorted alpha */
1448 (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
1449 (void) SetImageAlphaChannel(resize_alpha,DeactivateAlphaChannel);
1450 (void) CompositeImage(resize_image,CopyOpacityCompositeOp,resize_alpha,
1452 InheritException(exception,&resize_image->exception);
1453 resize_alpha=DestroyImage(resize_alpha);
1455 (void) SetImageVirtualPixelMethod(resize_image,vp_save);
1458 Clean up the results of the Distortion
1460 crop_area.width=columns;
1461 crop_area.height=rows;
1465 tmp_image=resize_image;
1466 resize_image=CropImage(tmp_image,&crop_area,exception);
1467 tmp_image=DestroyImage(tmp_image);
1469 if ( resize_image == (Image *) NULL )
1470 return((Image *) NULL);
1472 return(resize_image);
1476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480 % D i s t o r t I m a g e %
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486 % DistortImage() distorts an image using various distortion methods, by
1487 % mapping color lookups of the source image to a new destination image
1488 % usally of the same size as the source image, unless 'bestfit' is set to
1491 % If 'bestfit' is enabled, and distortion allows it, the destination image is
1492 % adjusted to ensure the whole source 'image' will just fit within the final
1493 % destination image, which will be sized and offset accordingly. Also in
1494 % many cases the virtual offset of the source image will be taken into
1495 % account in the mapping.
1497 % If the '-verbose' control option has been set print to standard error the
1498 % equicelent '-fx' formula with coefficients for the function, if practical.
1500 % The format of the DistortImage() method is:
1502 % Image *DistortImage(const Image *image,const DistortImageMethod method,
1503 % const size_t number_arguments,const double *arguments,
1504 % MagickBooleanType bestfit, ExceptionInfo *exception)
1506 % A description of each parameter follows:
1508 % o image: the image to be distorted.
1510 % o method: the method of image distortion.
1512 % ArcDistortion always ignores source image offset, and always
1513 % 'bestfit' the destination image with the top left corner offset
1514 % relative to the polar mapping center.
1516 % Affine, Perspective, and Bilinear, do least squares fitting of the
1517 % distrotion when more than the minimum number of control point pairs
1520 % Perspective, and Bilinear, fall back to a Affine distortion when less
1521 % than 4 control point pairs are provided. While Affine distortions
1522 % let you use any number of control point pairs, that is Zero pairs is
1523 % a No-Op (viewport only) distortion, one pair is a translation and
1524 % two pairs of control points do a scale-rotate-translate, without any
1527 % o number_arguments: the number of arguments given.
1529 % o arguments: an array of floating point arguments for this method.
1531 % o bestfit: Attempt to 'bestfit' the size of the resulting image.
1532 % This also forces the resulting image to be a 'layered' virtual
1533 % canvas image. Can be overridden using 'distort:viewport' setting.
1535 % o exception: return any errors or warnings in this structure
1537 % Extra Controls from Image meta-data (artifacts)...
1540 % Output to stderr alternatives, internal coefficents, and FX
1541 % equivelents for the distortion operation (if feasible).
1542 % This forms an extra check of the distortion method, and allows users
1543 % access to the internal constants IM calculates for the distortion.
1545 % o "distort:viewport"
1546 % Directly set the output image canvas area and offest to use for the
1547 % resulting image, rather than use the original images canvas, or a
1548 % calculated 'bestfit' canvas.
1551 % Scale the size of the output canvas by this amount to provide a
1552 % method of Zooming, and for super-sampling the results.
1554 % Other settings that can effect results include
1556 % o 'interpolate' For source image lookups (scale enlargements)
1558 % o 'filter' Set filter to use for area-resampling (scale shrinking).
1559 % Set to 'point' to turn off and use 'interpolate' lookup
1564 MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
1565 const size_t number_arguments,const double *arguments,
1566 MagickBooleanType bestfit,ExceptionInfo *exception)
1568 #define DistortImageTag "Distort/Image"
1578 geometry; /* geometry of the distorted space viewport */
1583 assert(image != (Image *) NULL);
1584 assert(image->signature == MagickSignature);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1587 assert(exception != (ExceptionInfo *) NULL);
1588 assert(exception->signature == MagickSignature);
1592 Handle Special Compound Distortions (in-direct distortions)
1594 if ( method == ResizeDistortion )
1596 if ( number_arguments != 2 )
1598 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1599 "InvalidArgument","%s : '%s'","Resize",
1600 "Invalid number of args: 2 only");
1601 return((Image *) NULL);
1603 distort_image=DistortResizeImage(image,(size_t)arguments[0],
1604 (size_t)arguments[1], exception);
1605 return(distort_image);
1609 Convert input arguments (usally as control points for reverse mapping)
1610 into mapping coefficients to apply the distortion.
1612 Note that some distortions are mapped to other distortions,
1613 and as such do not require specific code after this point.
1615 coeff = GenerateCoefficients(image, &method, number_arguments,
1616 arguments, 0, exception);
1617 if ( coeff == (double *) NULL )
1618 return((Image *) NULL);
1621 Determine the size and offset for a 'bestfit' destination.
1622 Usally the four corners of the source image is enough.
1625 /* default output image bounds, when no 'bestfit' is requested */
1626 geometry.width=image->columns;
1627 geometry.height=image->rows;
1631 if ( method == ArcDistortion ) {
1632 /* always use the 'best fit' viewport */
1633 bestfit = MagickTrue;
1636 /* Work out the 'best fit', (required for ArcDistortion) */
1641 s.x=s.y=min.x=max.x=min.y=max.y=0.0; /* keep compiler happy */
1643 /* defines to figure out the bounds of the distorted image */
1644 #define InitalBounds(p) \
1646 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1647 min.x = max.x = p.x; \
1648 min.y = max.y = p.y; \
1650 #define ExpandBounds(p) \
1652 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1653 min.x = MagickMin(min.x,p.x); \
1654 max.x = MagickMax(max.x,p.x); \
1655 min.y = MagickMin(min.y,p.y); \
1656 max.y = MagickMax(max.y,p.y); \
1661 case AffineDistortion:
1662 { double inverse[6];
1663 InvertAffineCoefficients(coeff, inverse);
1664 s.x = (double) image->page.x;
1665 s.y = (double) image->page.y;
1666 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1667 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1669 s.x = (double) image->page.x+image->columns;
1670 s.y = (double) image->page.y;
1671 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1672 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1674 s.x = (double) image->page.x;
1675 s.y = (double) image->page.y+image->rows;
1676 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1677 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1679 s.x = (double) image->page.x+image->columns;
1680 s.y = (double) image->page.y+image->rows;
1681 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1682 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1686 case PerspectiveDistortion:
1687 { double inverse[8], scale;
1688 InvertPerspectiveCoefficients(coeff, inverse);
1689 s.x = (double) image->page.x;
1690 s.y = (double) image->page.y;
1691 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1692 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1693 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1694 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1696 s.x = (double) image->page.x+image->columns;
1697 s.y = (double) image->page.y;
1698 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1699 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1700 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1701 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1703 s.x = (double) image->page.x;
1704 s.y = (double) image->page.y+image->rows;
1705 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1706 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1707 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1708 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1710 s.x = (double) image->page.x+image->columns;
1711 s.y = (double) image->page.y+image->rows;
1712 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1713 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1714 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1715 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1721 /* Forward Map Corners */
1722 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1726 d.x = (coeff[2]-coeff[3])*ca;
1727 d.y = (coeff[2]-coeff[3])*sa;
1729 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1733 d.x = (coeff[2]-coeff[3])*ca;
1734 d.y = (coeff[2]-coeff[3])*sa;
1736 /* Orthogonal points along top of arc */
1737 for( a=(double) (ceil((double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
1738 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1739 ca = cos(a); sa = sin(a);
1745 Convert the angle_to_width and radius_to_height
1746 to appropriate scaling factors, to allow faster processing
1747 in the mapping function.
1749 coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
1750 coeff[3] = (double)image->rows/coeff[3];
1753 case PolarDistortion:
1755 if (number_arguments < 2)
1756 coeff[2] = coeff[3] = 0.0;
1757 min.x = coeff[2]-coeff[0];
1758 max.x = coeff[2]+coeff[0];
1759 min.y = coeff[3]-coeff[0];
1760 max.y = coeff[3]+coeff[0];
1761 /* should be about 1.0 if Rmin = 0 */
1762 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1765 case DePolarDistortion:
1767 /* direct calculation as it needs to tile correctly
1768 * for reversibility in a DePolar-Polar cycle */
1769 geometry.x = geometry.y = 0;
1770 geometry.height = (size_t) ceil(coeff[0]-coeff[1]);
1771 geometry.width = (size_t)
1772 ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
1775 case ShepardsDistortion:
1776 case BilinearForwardDistortion:
1777 case BilinearReverseDistortion:
1779 case QuadrilateralDistortion:
1781 case PolynomialDistortion:
1782 case BarrelDistortion:
1783 case BarrelInverseDistortion:
1785 /* no bestfit available for this distortion */
1786 bestfit = MagickFalse;
1790 /* Set the output image geometry to calculated 'bestfit'.
1791 Yes this tends to 'over do' the file image size, ON PURPOSE!
1792 Do not do this for DePolar which needs to be exact for virtual tiling.
1794 if ( bestfit && method != DePolarDistortion ) {
1795 geometry.x = (ssize_t) floor(min.x-0.5);
1796 geometry.y = (ssize_t) floor(min.y-0.5);
1797 geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
1798 geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
1801 /* Now that we have a new size lets some distortions to it exactly
1802 This is for correct handling of Depolar and its virtual tile handling
1804 if ( method == DePolarDistortion ) {
1805 coeff[6]=(coeff[5]-coeff[4])/geometry.width; /* changed width */
1806 coeff[7]=(coeff[0]-coeff[1])/geometry.height; /* should be about 1.0 */
1810 /* The user provided a 'viewport' expert option which may
1811 overrides some parts of the current output image geometry.
1812 For ArcDistortion, this also overrides its default 'bestfit' setting.
1814 { const char *artifact=GetImageArtifact(image,"distort:viewport");
1815 viewport_given = MagickFalse;
1816 if ( artifact != (const char *) NULL ) {
1817 (void) ParseAbsoluteGeometry(artifact,&geometry);
1818 viewport_given = MagickTrue;
1822 /* Verbose output */
1823 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
1826 char image_gen[MaxTextExtent];
1829 /* Set destination image size and virtual offset */
1830 if ( bestfit || viewport_given ) {
1831 (void) FormatMagickString(image_gen, MaxTextExtent," -size %.20gx%.20g "
1832 "-page %+.20g%+.20g xc: +insert \\\n",(double) geometry.width,
1833 (double) geometry.height,(double) geometry.x,(double) geometry.y);
1834 lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
1837 image_gen[0] = '\0'; /* no destination to generate */
1838 lookup = "p{ xx-page.x-.5, yy-page.x-.5 }"; /* simplify lookup */
1842 case AffineDistortion:
1846 inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
1847 if (inverse == (double *) NULL) {
1848 coeff = (double *) RelinquishMagickMemory(coeff);
1849 (void) ThrowMagickException(exception,GetMagickModule(),
1850 ResourceLimitError,"MemoryAllocationFailed",
1851 "%s", "DistortImages");
1852 return((Image *) NULL);
1854 InvertAffineCoefficients(coeff, inverse);
1855 CoefficientsToAffineArgs(inverse);
1856 fprintf(stderr, "Affine Projection:\n");
1857 fprintf(stderr, " -distort AffineProjection \\\n '");
1859 fprintf(stderr, "%lf,", inverse[i]);
1860 fprintf(stderr, "%lf'\n", inverse[5]);
1861 inverse = (double *) RelinquishMagickMemory(inverse);
1863 fprintf(stderr, "Affine Distort, FX Equivelent:\n");
1864 fprintf(stderr, "%s", image_gen);
1865 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1866 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
1867 coeff[0], coeff[1], coeff[2]);
1868 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
1869 coeff[3], coeff[4], coeff[5]);
1870 fprintf(stderr, " %s'\n", lookup);
1875 case PerspectiveDistortion:
1879 inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
1880 if (inverse == (double *) NULL) {
1881 coeff = (double *) RelinquishMagickMemory(coeff);
1882 (void) ThrowMagickException(exception,GetMagickModule(),
1883 ResourceLimitError,"MemoryAllocationFailed",
1884 "%s", "DistortCoefficients");
1885 return((Image *) NULL);
1887 InvertPerspectiveCoefficients(coeff, inverse);
1888 fprintf(stderr, "Perspective Projection:\n");
1889 fprintf(stderr, " -distort PerspectiveProjection \\\n '");
1891 fprintf(stderr, "%lf, ", inverse[i]);
1892 fprintf(stderr, "\n ");
1894 fprintf(stderr, "%lf, ", inverse[i]);
1895 fprintf(stderr, "%lf'\n", inverse[7]);
1896 inverse = (double *) RelinquishMagickMemory(inverse);
1898 fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
1899 fprintf(stderr, "%s", image_gen);
1900 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1901 fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
1902 coeff[6], coeff[7]);
1903 fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1904 coeff[0], coeff[1], coeff[2]);
1905 fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1906 coeff[3], coeff[4], coeff[5]);
1907 fprintf(stderr, " rr%s0 ? %s : blue'\n",
1908 coeff[8] < 0 ? "<" : ">", lookup);
1912 case BilinearForwardDistortion:
1913 fprintf(stderr, "BilinearForward Mapping Equations:\n");
1914 fprintf(stderr, "%s", image_gen);
1915 fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1916 coeff[0], coeff[1], coeff[2], coeff[3]);
1917 fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1918 coeff[4], coeff[5], coeff[6], coeff[7]);
1921 fprintf(stderr, " c8 = %+lf c9 = 2*a = %+lf;\n",
1922 coeff[8], coeff[9]);
1924 fprintf(stderr, "BilinearForward Distort, FX Equivelent:\n");
1925 fprintf(stderr, "%s", image_gen);
1926 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
1927 0.5-coeff[3], 0.5-coeff[7]);
1928 fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
1929 coeff[6], -coeff[2], coeff[8]);
1930 /* Handle Special degenerate (non-quadratic) or trapezoidal case */
1931 if ( coeff[9] != 0 ) {
1932 fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
1933 -2*coeff[9], coeff[4], -coeff[0]);
1934 fprintf(stderr, " yy=( -bb + sqrt(rt) ) / %lf;\n",
1937 fprintf(stderr, " yy=(%lf*ii%+lf*jj)/bb;\n",
1938 -coeff[4], coeff[0]);
1939 fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
1940 -coeff[1], coeff[0], coeff[2]);
1941 if ( coeff[9] != 0 )
1942 fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
1944 fprintf(stderr, " %s'\n", lookup);
1947 case BilinearReverseDistortion:
1949 fprintf(stderr, "Polynomial Projection Distort:\n");
1950 fprintf(stderr, " -distort PolynomialProjection \\\n");
1951 fprintf(stderr, " '1.5, %lf, %lf, %lf, %lf,\n",
1952 coeff[3], coeff[0], coeff[1], coeff[2]);
1953 fprintf(stderr, " %lf, %lf, %lf, %lf'\n",
1954 coeff[7], coeff[4], coeff[5], coeff[6]);
1956 fprintf(stderr, "BilinearReverse Distort, FX Equivelent:\n");
1957 fprintf(stderr, "%s", image_gen);
1958 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1959 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1960 coeff[0], coeff[1], coeff[2], coeff[3]);
1961 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1962 coeff[4], coeff[5], coeff[6], coeff[7]);
1963 fprintf(stderr, " %s'\n", lookup);
1966 case PolynomialDistortion:
1968 size_t nterms = (size_t) coeff[1];
1969 fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
1970 coeff[0],(unsigned long) nterms);
1971 fprintf(stderr, "%s", image_gen);
1972 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1973 fprintf(stderr, " xx =");
1974 for (i=0; i<(ssize_t) nterms; i++) {
1975 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1976 fprintf(stderr, " %+lf%s", coeff[2+i],
1979 fprintf(stderr, ";\n yy =");
1980 for (i=0; i<(ssize_t) nterms; i++) {
1981 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1982 fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
1985 fprintf(stderr, ";\n %s'\n", lookup);
1990 fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
1991 for ( i=0; i<5; i++ )
1992 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
1993 fprintf(stderr, "Arc Distort, FX Equivelent:\n");
1994 fprintf(stderr, "%s", image_gen);
1995 fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
1996 fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
1998 fprintf(stderr, " xx=xx-round(xx);\n");
1999 fprintf(stderr, " xx=xx*%lf %+lf;\n",
2000 coeff[1], coeff[4]);
2001 fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
2002 coeff[2], coeff[3]);
2003 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
2006 case PolarDistortion:
2008 fprintf(stderr, "Polar Distort, Internal Coefficents\n");
2009 for ( i=0; i<8; i++ )
2010 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
2011 fprintf(stderr, "Polar Distort, FX Equivelent:\n");
2012 fprintf(stderr, "%s", image_gen);
2013 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
2014 -coeff[2], -coeff[3]);
2015 fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
2016 -(coeff[4]+coeff[5])/2 );
2017 fprintf(stderr, " xx=xx-round(xx);\n");
2018 fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
2020 fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
2021 -coeff[1], coeff[7] );
2022 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
2025 case DePolarDistortion:
2027 fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
2028 for ( i=0; i<8; i++ )
2029 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
2030 fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
2031 fprintf(stderr, "%s", image_gen);
2032 fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
2033 fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
2034 fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
2035 fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
2036 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
2039 case BarrelDistortion:
2040 case BarrelInverseDistortion:
2042 /* NOTE: This does the barrel roll in pixel coords not image coords
2043 ** The internal distortion must do it in image coordinates,
2044 ** so that is what the center coeff (8,9) is given in.
2046 xc = ((double)image->columns-1.0)/2.0 + image->page.x;
2047 yc = ((double)image->rows-1.0)/2.0 + image->page.y;
2048 fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
2049 method == BarrelDistortion ? "" : "Inv");
2050 fprintf(stderr, "%s", image_gen);
2051 if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
2052 fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
2054 fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
2055 coeff[8]-0.5, coeff[9]-0.5);
2057 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
2058 fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2059 method == BarrelDistortion ? "*" : "/",
2060 coeff[0],coeff[1],coeff[2],coeff[3]);
2061 fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2062 method == BarrelDistortion ? "*" : "/",
2063 coeff[4],coeff[5],coeff[6],coeff[7]);
2064 fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
2071 /* The user provided a 'scale' expert option will scale the
2072 output image size, by the factor given allowing for super-sampling
2073 of the distorted image space. Any scaling factors must naturally
2074 be halved as a result.
2076 { const char *artifact;
2077 artifact=GetImageArtifact(image,"distort:scale");
2078 output_scaling = 1.0;
2079 if (artifact != (const char *) NULL) {
2080 output_scaling = fabs(StringToDouble(artifact));
2081 geometry.width *= output_scaling;
2082 geometry.height *= output_scaling;
2083 geometry.x *= output_scaling;
2084 geometry.y *= output_scaling;
2085 if ( output_scaling < 0.1 ) {
2086 coeff = (double *) RelinquishMagickMemory(coeff);
2087 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2088 "InvalidArgument","%s", "-set option:distort:scale" );
2089 return((Image *) NULL);
2091 output_scaling = 1/output_scaling;
2094 #define ScaleFilter(F,A,B,C,D) \
2095 ScaleResampleFilter( (F), \
2096 output_scaling*(A), output_scaling*(B), \
2097 output_scaling*(C), output_scaling*(D) )
2100 Initialize the distort image attributes.
2102 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
2104 if (distort_image == (Image *) NULL)
2105 return((Image *) NULL);
2106 /* if image is ColorMapped - change it to DirectClass */
2107 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
2109 InheritException(exception,&distort_image->exception);
2110 distort_image=DestroyImage(distort_image);
2111 return((Image *) NULL);
2113 distort_image->page.x=geometry.x;
2114 distort_image->page.y=geometry.y;
2115 if (distort_image->background_color.opacity != OpaqueOpacity)
2116 distort_image->matte=MagickTrue;
2118 { /* ----- MAIN CODE -----
2119 Sample the source image to each pixel in the distort image.
2134 **restrict resample_filter;
2141 GetMagickPixelPacket(distort_image,&zero);
2142 resample_filter=AcquireResampleFilterThreadSet(image,
2143 UndefinedVirtualPixelMethod,MagickFalse,exception);
2144 distort_view=AcquireCacheView(distort_image);
2145 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2146 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2148 for (j=0; j < (ssize_t) distort_image->rows; j++)
2151 id = GetOpenMPThreadId();
2154 validity; /* how mathematically valid is this the mapping */
2160 pixel, /* pixel color to assign to distorted image */
2161 invalid; /* the color to assign when distort result is invalid */
2165 s; /* transform destination image x,y to source image x,y */
2167 register IndexPacket
2173 register PixelPacket
2176 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
2178 if (q == (PixelPacket *) NULL)
2183 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
2186 /* Define constant scaling vectors for Affine Distortions
2187 Other methods are either variable, or use interpolated lookup
2191 case AffineDistortion:
2192 ScaleFilter( resample_filter[id],
2194 coeff[3], coeff[4] );
2200 /* Initialize default pixel validity
2201 * negative: pixel is invalid output 'matte_color'
2202 * 0.0 to 1.0: antialiased, mix with resample output
2203 * 1.0 or greater: use resampled output.
2207 GetMagickPixelPacket(distort_image,&invalid);
2208 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
2209 (IndexPacket *) NULL, &invalid);
2210 if (distort_image->colorspace == CMYKColorspace)
2211 ConvertRGBToCMYK(&invalid); /* what about other color spaces? */
2213 for (i=0; i < (ssize_t) distort_image->columns; i++)
2215 /* map pixel coordinate to distortion space coordinate */
2216 d.x = (double) (geometry.x+i+0.5)*output_scaling;
2217 d.y = (double) (geometry.y+j+0.5)*output_scaling;
2218 s = d; /* default is a no-op mapping */
2221 case AffineDistortion:
2223 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2224 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2225 /* Affine partial derivitives are constant -- set above */
2228 case PerspectiveDistortion:
2231 p,q,r,abs_r,abs_c6,abs_c7,scale;
2232 /* perspective is a ratio of affines */
2233 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2234 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2235 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2236 /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
2237 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2238 /* Determine horizon anti-alias blending */
2240 abs_c6 = fabs(coeff[6]);
2241 abs_c7 = fabs(coeff[7]);
2242 if ( abs_c6 > abs_c7 ) {
2243 if ( abs_r < abs_c6*output_scaling )
2244 validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
2246 else if ( abs_r < abs_c7*output_scaling )
2247 validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
2248 /* Perspective Sampling Point (if valid) */
2249 if ( validity > 0.0 ) {
2250 /* divide by r affine, for perspective scaling */
2254 /* Perspective Partial Derivatives or Scaling Vectors */
2256 ScaleFilter( resample_filter[id],
2257 (r*coeff[0] - p*coeff[6])*scale,
2258 (r*coeff[1] - p*coeff[7])*scale,
2259 (r*coeff[3] - q*coeff[6])*scale,
2260 (r*coeff[4] - q*coeff[7])*scale );
2264 case BilinearReverseDistortion:
2266 /* Reversed Mapped is just a simple polynomial */
2267 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2268 s.y=coeff[4]*d.x+coeff[5]*d.y
2269 +coeff[6]*d.x*d.y+coeff[7];
2270 /* Bilinear partial derivitives of scaling vectors */
2271 ScaleFilter( resample_filter[id],
2272 coeff[0] + coeff[2]*d.y,
2273 coeff[1] + coeff[2]*d.x,
2274 coeff[4] + coeff[6]*d.y,
2275 coeff[5] + coeff[6]*d.x );
2278 case BilinearForwardDistortion:
2280 /* Forward mapped needs reversed polynomial equations
2281 * which unfortunatally requires a square root! */
2283 d.x -= coeff[3]; d.y -= coeff[7];
2284 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2285 c = coeff[4]*d.x - coeff[0]*d.y;
2288 /* Handle Special degenerate (non-quadratic) case */
2289 if ( fabs(coeff[9]) < MagickEpsilon )
2292 c = b*b - 2*coeff[9]*c;
2296 s.y = ( -b + sqrt(c) )/coeff[9];
2298 if ( validity > 0.0 )
2299 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2301 /* NOTE: the sign of the square root should be -ve for parts
2302 where the source image becomes 'flipped' or 'mirrored'.
2303 FUTURE: Horizon handling
2304 FUTURE: Scaling factors or Deritives (how?)
2309 case BilinearDistortion:
2310 /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
2311 /* UNDER DEVELOPMENT */
2314 case PolynomialDistortion:
2316 /* multi-ordered polynomial */
2321 nterms=(ssize_t)coeff[1];
2324 du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
2326 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2327 for(k=0; k < nterms; k++) {
2328 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2329 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2330 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2331 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2332 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2333 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2335 ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
2340 /* what is the angle and radius in the destination image */
2341 s.x = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
2342 s.x -= MagickRound(s.x); /* angle */
2343 s.y = hypot(d.x,d.y); /* radius */
2345 /* Arc Distortion Partial Scaling Vectors
2346 Are derived by mapping the perpendicular unit vectors
2347 dR and dA*R*2PI rather than trying to map dx and dy
2348 The results is a very simple orthogonal aligned ellipse.
2350 if ( s.y > MagickEpsilon )
2351 ScaleFilter( resample_filter[id],
2352 (double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
2354 ScaleFilter( resample_filter[id],
2355 distort_image->columns*2, 0, 0, coeff[3] );
2357 /* now scale the angle and radius for source image lookup point */
2358 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2359 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2362 case PolarDistortion:
2363 { /* Rect/Cartesain/Cylinder to Polar View */
2366 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2368 s.x -= MagickRound(s.x);
2369 s.x *= Magick2PI; /* angle - relative to centerline */
2370 s.y = hypot(d.x,d.y); /* radius */
2372 /* Polar Scaling vectors are based on mapping dR and dA vectors
2373 This results in very simple orthogonal scaling vectors
2375 if ( s.y > MagickEpsilon )
2376 ScaleFilter( resample_filter[id],
2377 (double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
2379 ScaleFilter( resample_filter[id],
2380 distort_image->columns*2, 0, 0, coeff[7] );
2382 /* now finish mapping radius/angle to source x,y coords */
2383 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2384 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2387 case DePolarDistortion:
2388 { /* Polar to Cylindical */
2389 /* ignore all destination virtual offsets */
2390 d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
2391 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2392 s.x = d.y*sin(d.x) + coeff[2];
2393 s.y = d.y*cos(d.x) + coeff[3];
2394 /* derivatives are usless - better to use SuperSampling */
2397 case BarrelDistortion:
2398 case BarrelInverseDistortion:
2400 double r,fx,fy,gx,gy;
2401 /* Radial Polynomial Distortion (de-normalized) */
2404 r = sqrt(d.x*d.x+d.y*d.y);
2405 if ( r > MagickEpsilon ) {
2406 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2407 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2408 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2409 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2410 /* adjust functions and scaling for 'inverse' form */
2411 if ( method == BarrelInverseDistortion ) {
2412 fx = 1/fx; fy = 1/fy;
2413 gx *= -fx*fx; gy *= -fy*fy;
2415 /* Set the source pixel to lookup and EWA derivative vectors */
2416 s.x = d.x*fx + coeff[8];
2417 s.y = d.y*fy + coeff[9];
2418 ScaleFilter( resample_filter[id],
2419 gx*d.x*d.x + fx, gx*d.x*d.y,
2420 gy*d.x*d.y, gy*d.y*d.y + fy );
2423 /* Special handling to avoid divide by zero when r==0
2425 ** The source and destination pixels match in this case
2426 ** which was set at the top of the loop using s = d;
2427 ** otherwise... s.x=coeff[8]; s.y=coeff[9];
2429 if ( method == BarrelDistortion )
2430 ScaleFilter( resample_filter[id],
2431 coeff[3], 0, 0, coeff[7] );
2432 else /* method == BarrelInverseDistortion */
2433 /* FUTURE, trap for D==0 causing division by zero */
2434 ScaleFilter( resample_filter[id],
2435 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2439 case ShepardsDistortion:
2440 { /* Shepards Method, or Inverse Weighted Distance for
2441 displacement around the destination image control points
2442 The input arguments are the coefficents to the function.
2443 This is more of a 'displacement' function rather than an
2444 absolute distortion function.
2451 denominator = s.x = s.y = 0;
2452 for(i=0; i<number_arguments; i+=4) {
2454 ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
2455 + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
2461 s.x += (arguments[ i ]-arguments[i+2])*weight;
2462 s.y += (arguments[i+1]-arguments[i+3])*weight;
2463 denominator += weight;
2470 /* We can not determine derivatives using shepards method
2471 only color interpolatation, not area-resampling */
2475 break; /* use the default no-op given above */
2477 /* map virtual canvas location back to real image coordinate */
2478 if ( bestfit && method != ArcDistortion ) {
2479 s.x -= image->page.x;
2480 s.y -= image->page.y;
2485 if ( validity <= 0.0 ) {
2486 /* result of distortion is an invalid pixel - don't resample */
2487 SetPixelPacket(distort_image,&invalid,q,indexes);
2490 /* resample the source image to find its correct color */
2491 (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
2492 /* if validity between 0.0 and 1.0 mix result with invalid pixel */
2493 if ( validity < 1.0 ) {
2494 /* Do a blend of sample color and invalid pixel */
2495 /* should this be a 'Blend', or an 'Over' compose */
2496 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
2499 SetPixelPacket(distort_image,&pixel,q,indexes);
2504 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2505 if (sync == MagickFalse)
2507 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2512 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2513 #pragma omp critical (MagickCore_DistortImage)
2515 proceed=SetImageProgress(image,DistortImageTag,progress++,
2517 if (proceed == MagickFalse)
2521 fprintf(stderr, "\n");
2524 distort_view=DestroyCacheView(distort_view);
2525 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
2527 if (status == MagickFalse)
2528 distort_image=DestroyImage(distort_image);
2531 /* Arc does not return an offset unless 'bestfit' is in effect
2532 And the user has not provided an overriding 'viewport'.
2534 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2535 distort_image->page.x = 0;
2536 distort_image->page.y = 0;
2538 coeff = (double *) RelinquishMagickMemory(coeff);
2539 return(distort_image);
2543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2547 % S p a r s e C o l o r I m a g e %
2551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2553 % SparseColorImage(), given a set of coordinates, interpolates the colors
2554 % found at those coordinates, across the whole image, using various methods.
2556 % The format of the SparseColorImage() method is:
2558 % Image *SparseColorImage(const Image *image,const ChannelType channel,
2559 % const SparseColorMethod method,const size_t number_arguments,
2560 % const double *arguments,ExceptionInfo *exception)
2562 % A description of each parameter follows:
2564 % o image: the image to be filled in.
2566 % o channel: Specify which color values (in RGBKA sequence) are being set.
2567 % This also determines the number of color_values in above.
2569 % o method: the method to fill in the gradient between the control points.
2571 % The methods used for SparseColor() are often simular to methods
2572 % used for DistortImage(), and even share the same code for determination
2573 % of the function coefficents, though with more dimensions (or resulting
2576 % o number_arguments: the number of arguments given.
2578 % o arguments: array of floating point arguments for this method--
2579 % x,y,color_values-- with color_values given as normalized values.
2581 % o exception: return any errors or warnings in this structure
2584 MagickExport Image *SparseColorImage(const Image *image,
2585 const ChannelType channel,const SparseColorMethod method,
2586 const size_t number_arguments,const double *arguments,
2587 ExceptionInfo *exception)
2589 #define SparseColorTag "Distort/SparseColor"
2603 assert(image != (Image *) NULL);
2604 assert(image->signature == MagickSignature);
2605 if (image->debug != MagickFalse)
2606 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2607 assert(exception != (ExceptionInfo *) NULL);
2608 assert(exception->signature == MagickSignature);
2610 /* Determine number of color values needed per control point */
2612 if ( channel & RedChannel ) number_colors++;
2613 if ( channel & GreenChannel ) number_colors++;
2614 if ( channel & BlueChannel ) number_colors++;
2615 if ( channel & IndexChannel ) number_colors++;
2616 if ( channel & OpacityChannel ) number_colors++;
2619 Convert input arguments into mapping coefficients to apply the distortion.
2620 Note some Methods may fall back to other simpler methods.
2622 distort_method=(DistortImageMethod) method;
2623 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
2624 arguments, number_colors, exception);
2625 if ( coeff == (double *) NULL )
2626 return((Image *) NULL);
2628 /* Verbose output */
2629 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
2632 case BarycentricColorInterpolate:
2634 register ssize_t x=0;
2635 fprintf(stderr, "Barycentric Sparse Color:\n");
2636 if ( channel & RedChannel )
2637 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
2638 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2639 if ( channel & GreenChannel )
2640 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
2641 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2642 if ( channel & BlueChannel )
2643 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
2644 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2645 if ( channel & IndexChannel )
2646 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
2647 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2648 if ( channel & OpacityChannel )
2649 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
2650 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2653 case BilinearColorInterpolate:
2655 register ssize_t x=0;
2656 fprintf(stderr, "Bilinear Sparse Color\n");
2657 if ( channel & RedChannel )
2658 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2659 coeff[ x ], coeff[x+1],
2660 coeff[x+2], coeff[x+3]),x+=4;
2661 if ( channel & GreenChannel )
2662 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2663 coeff[ x ], coeff[x+1],
2664 coeff[x+2], coeff[x+3]),x+=4;
2665 if ( channel & BlueChannel )
2666 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2667 coeff[ x ], coeff[x+1],
2668 coeff[x+2], coeff[x+3]),x+=4;
2669 if ( channel & IndexChannel )
2670 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2671 coeff[ x ], coeff[x+1],
2672 coeff[x+2], coeff[x+3]),x+=4;
2673 if ( channel & OpacityChannel )
2674 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2675 coeff[ x ], coeff[x+1],
2676 coeff[x+2], coeff[x+3]),x+=4;
2680 /* unknown, or which are too complex for FX alturnatives */
2685 /* Generate new image for generated interpolated gradient.
2686 * ASIDE: Actually we could have just replaced the colors of the original
2687 * image, but IM core policy, is if storage class could change then clone
2691 sparse_image=CloneImage(image,0,0,MagickTrue,exception);
2692 if (sparse_image == (Image *) NULL)
2693 return((Image *) NULL);
2694 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
2695 { /* if image is ColorMapped - change it to DirectClass */
2696 InheritException(exception,&image->exception);
2697 sparse_image=DestroyImage(sparse_image);
2698 return((Image *) NULL);
2700 { /* ----- MAIN CODE ----- */
2715 sparse_view=AcquireCacheView(sparse_image);
2716 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2717 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2719 for (j=0; j < (ssize_t) sparse_image->rows; j++)
2725 pixel; /* pixel to assign to distorted image */
2727 register IndexPacket
2733 register PixelPacket
2736 q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
2738 if (q == (PixelPacket *) NULL)
2743 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
2744 GetMagickPixelPacket(sparse_image,&pixel);
2745 for (i=0; i < (ssize_t) image->columns; i++)
2747 SetMagickPixelPacket(image,q,indexes,&pixel);
2750 case BarycentricColorInterpolate:
2752 register ssize_t x=0;
2753 if ( channel & RedChannel )
2754 pixel.red = coeff[x]*i +coeff[x+1]*j
2756 if ( channel & GreenChannel )
2757 pixel.green = coeff[x]*i +coeff[x+1]*j
2759 if ( channel & BlueChannel )
2760 pixel.blue = coeff[x]*i +coeff[x+1]*j
2762 if ( channel & IndexChannel )
2763 pixel.index = coeff[x]*i +coeff[x+1]*j
2765 if ( channel & OpacityChannel )
2766 pixel.opacity = coeff[x]*i +coeff[x+1]*j
2770 case BilinearColorInterpolate:
2772 register ssize_t x=0;
2773 if ( channel & RedChannel )
2774 pixel.red = coeff[x]*i + coeff[x+1]*j +
2775 coeff[x+2]*i*j + coeff[x+3], x+=4;
2776 if ( channel & GreenChannel )
2777 pixel.green = coeff[x]*i + coeff[x+1]*j +
2778 coeff[x+2]*i*j + coeff[x+3], x+=4;
2779 if ( channel & BlueChannel )
2780 pixel.blue = coeff[x]*i + coeff[x+1]*j +
2781 coeff[x+2]*i*j + coeff[x+3], x+=4;
2782 if ( channel & IndexChannel )
2783 pixel.index = coeff[x]*i + coeff[x+1]*j +
2784 coeff[x+2]*i*j + coeff[x+3], x+=4;
2785 if ( channel & OpacityChannel )
2786 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
2787 coeff[x+2]*i*j + coeff[x+3], x+=4;
2790 case ShepardsColorInterpolate:
2791 { /* Shepards Method, uses its own input arguments as coefficients.
2798 if ( channel & RedChannel ) pixel.red = 0.0;
2799 if ( channel & GreenChannel ) pixel.green = 0.0;
2800 if ( channel & BlueChannel ) pixel.blue = 0.0;
2801 if ( channel & IndexChannel ) pixel.index = 0.0;
2802 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
2804 for(k=0; k<number_arguments; k+=2+number_colors) {
2805 register ssize_t x=(ssize_t) k+2;
2807 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2808 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2813 if ( channel & RedChannel )
2814 pixel.red += arguments[x++]*weight;
2815 if ( channel & GreenChannel )
2816 pixel.green += arguments[x++]*weight;
2817 if ( channel & BlueChannel )
2818 pixel.blue += arguments[x++]*weight;
2819 if ( channel & IndexChannel )
2820 pixel.index += arguments[x++]*weight;
2821 if ( channel & OpacityChannel )
2822 pixel.opacity += arguments[x++]*weight;
2823 denominator += weight;
2825 if ( channel & RedChannel ) pixel.red /= denominator;
2826 if ( channel & GreenChannel ) pixel.green /= denominator;
2827 if ( channel & BlueChannel ) pixel.blue /= denominator;
2828 if ( channel & IndexChannel ) pixel.index /= denominator;
2829 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
2832 case VoronoiColorInterpolate:
2834 { /* Just use the closest control point you can find! */
2838 minimum = MagickHuge;
2840 for(k=0; k<number_arguments; k+=2+number_colors) {
2842 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2843 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2844 if ( distance < minimum ) {
2845 register ssize_t x=(ssize_t) k+2;
2846 if ( channel & RedChannel ) pixel.red = arguments[x++];
2847 if ( channel & GreenChannel ) pixel.green = arguments[x++];
2848 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
2849 if ( channel & IndexChannel ) pixel.index = arguments[x++];
2850 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
2857 /* set the color directly back into the source image */
2858 if ( channel & RedChannel ) pixel.red *= QuantumRange;
2859 if ( channel & GreenChannel ) pixel.green *= QuantumRange;
2860 if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
2861 if ( channel & IndexChannel ) pixel.index *= QuantumRange;
2862 if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
2863 SetPixelPacket(sparse_image,&pixel,q,indexes);
2867 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
2868 if (sync == MagickFalse)
2870 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2875 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2876 #pragma omp critical (MagickCore_SparseColorImage)
2878 proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
2879 if (proceed == MagickFalse)
2883 sparse_view=DestroyCacheView(sparse_view);
2884 if (status == MagickFalse)
2885 sparse_image=DestroyImage(sparse_image);
2887 coeff = (double *) RelinquishMagickMemory(coeff);
2888 return(sparse_image);