43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/channel.h"
48#include "MagickCore/colorspace-private.h"
49#include "MagickCore/composite-private.h"
50#include "MagickCore/distort.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/gem.h"
54#include "MagickCore/image.h"
55#include "MagickCore/linked-list.h"
56#include "MagickCore/list.h"
57#include "MagickCore/matrix.h"
58#include "MagickCore/matrix-private.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/option.h"
62#include "MagickCore/pixel.h"
63#include "MagickCore/pixel-accessor.h"
64#include "MagickCore/resample.h"
65#include "MagickCore/resample-private.h"
66#include "MagickCore/registry.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/semaphore.h"
69#include "MagickCore/shear.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/string-private.h"
72#include "MagickCore/thread-private.h"
73#include "MagickCore/token.h"
74#include "MagickCore/transform.h"
79static inline void AffineArgsToCoefficients(
double *affine)
83 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
84 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
87static inline void CoefficientsToAffineArgs(
double *coeff)
91 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
92 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
94static void InvertAffineCoefficients(
const double *coeff,
double *inverse)
99 determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
100 inverse[0]=determinant*coeff[4];
101 inverse[1]=determinant*(-coeff[1]);
102 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
103 inverse[3]=determinant*(-coeff[3]);
104 inverse[4]=determinant*coeff[0];
105 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
108static void InvertPerspectiveCoefficients(
const double *coeff,
114 determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
115 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
116 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
117 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
118 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
119 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
120 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
121 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
122 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
144static size_t poly_number_terms(
double order)
147 if ( order < 1 || order > 5 ||
148 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
150 return((
size_t) floor((order+1)*(order+2)/2));
153static double poly_basis_fn(ssize_t n,
double x,
double y)
157 case 0:
return( 1.0 );
160 case 3:
return( x*y );
161 case 4:
return( x*x );
162 case 5:
return( y*y );
163 case 6:
return( x*x*x );
164 case 7:
return( x*x*y );
165 case 8:
return( x*y*y );
166 case 9:
return( y*y*y );
167 case 10:
return( x*x*x*x );
168 case 11:
return( x*x*x*y );
169 case 12:
return( x*x*y*y );
170 case 13:
return( x*y*y*y );
171 case 14:
return( y*y*y*y );
172 case 15:
return( x*x*x*x*x );
173 case 16:
return( x*x*x*x*y );
174 case 17:
return( x*x*x*y*y );
175 case 18:
return( x*x*y*y*y );
176 case 19:
return( x*y*y*y*y );
177 case 20:
return( y*y*y*y*y );
181static const char *poly_basis_str(ssize_t n)
186 case 1:
return(
"*ii");
187 case 2:
return(
"*jj");
188 case 3:
return(
"*ii*jj");
189 case 4:
return(
"*ii*ii");
190 case 5:
return(
"*jj*jj");
191 case 6:
return(
"*ii*ii*ii");
192 case 7:
return(
"*ii*ii*jj");
193 case 8:
return(
"*ii*jj*jj");
194 case 9:
return(
"*jj*jj*jj");
195 case 10:
return(
"*ii*ii*ii*ii");
196 case 11:
return(
"*ii*ii*ii*jj");
197 case 12:
return(
"*ii*ii*jj*jj");
198 case 13:
return(
"*ii*jj*jj*jj");
199 case 14:
return(
"*jj*jj*jj*jj");
200 case 15:
return(
"*ii*ii*ii*ii*ii");
201 case 16:
return(
"*ii*ii*ii*ii*jj");
202 case 17:
return(
"*ii*ii*ii*jj*jj");
203 case 18:
return(
"*ii*ii*jj*jj*jj");
204 case 19:
return(
"*ii*jj*jj*jj*jj");
205 case 20:
return(
"*jj*jj*jj*jj*jj");
209static double poly_basis_dx(ssize_t n,
double x,
double y)
213 case 0:
return( 0.0 );
214 case 1:
return( 1.0 );
215 case 2:
return( 0.0 );
218 case 5:
return( 0.0 );
219 case 6:
return( x*x );
220 case 7:
return( x*y );
221 case 8:
return( y*y );
222 case 9:
return( 0.0 );
223 case 10:
return( x*x*x );
224 case 11:
return( x*x*y );
225 case 12:
return( x*y*y );
226 case 13:
return( y*y*y );
227 case 14:
return( 0.0 );
228 case 15:
return( x*x*x*x );
229 case 16:
return( x*x*x*y );
230 case 17:
return( x*x*y*y );
231 case 18:
return( x*y*y*y );
232 case 19:
return( y*y*y*y );
233 case 20:
return( 0.0 );
237static double poly_basis_dy(ssize_t n,
double x,
double y)
241 case 0:
return( 0.0 );
242 case 1:
return( 0.0 );
243 case 2:
return( 1.0 );
245 case 4:
return( 0.0 );
247 default:
return( poly_basis_dx(n-1,x,y) );
283MagickExport
Image *AffineTransformImage(
const Image *image,
295 assert(image->signature == MagickCoreSignature);
298 assert(exception->signature == MagickCoreSignature);
299 if (IsEventLogging() != MagickFalse)
300 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
301 distort[0]=affine_matrix->sx;
302 distort[1]=affine_matrix->rx;
303 distort[2]=affine_matrix->ry;
304 distort[3]=affine_matrix->sy;
305 distort[4]=affine_matrix->tx;
306 distort[5]=affine_matrix->ty;
307 deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
308 MagickTrue,exception);
309 return(deskew_image);
362static inline double MagickRound(
double x)
367 if ((x-floor(x)) < (ceil(x)-x))
372static double *GenerateCoefficients(
const Image *image,
373 DistortMethod *method,
const size_t number_arguments,
const double *arguments,
389 if ( number_values == 0 ) {
405 cp_size = number_values+2;
410 if ( number_arguments < 4*cp_size &&
411 ( *method == BilinearForwardDistortion
412 || *method == BilinearReverseDistortion
413 || *method == PerspectiveDistortion
415 *method = AffineDistortion;
417 number_coefficients=0;
419 case AffineDistortion:
420 case RigidAffineDistortion:
422 number_coefficients=3*number_values;
424 case PolynomialDistortion:
426 i = poly_number_terms(arguments[0]);
427 number_coefficients = 2 + i*number_values;
429 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
430 "InvalidArgument",
"%s : '%s'",
"Polynomial",
431 "Invalid order, should be integer 1 to 5, or 1.5");
432 return((
double *) NULL);
434 if ((number_arguments < (1+i*cp_size)) ||
435 (((number_arguments-1) % cp_size) != 0)) {
436 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
437 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
438 "Polynomial", (double) i);
439 return((
double *) NULL);
442 case BilinearReverseDistortion:
443 number_coefficients=4*number_values;
448 case BilinearForwardDistortion:
449 number_coefficients=10;
455 case QuadrilateralDistortion:
456 number_coefficients=19;
459 case ShepardsDistortion:
460 number_coefficients=1;
463 number_coefficients=5;
465 case ScaleRotateTranslateDistortion:
466 case AffineProjectionDistortion:
467 case Plane2CylinderDistortion:
468 case Cylinder2PlaneDistortion:
469 number_coefficients=6;
471 case PolarDistortion:
472 case DePolarDistortion:
473 number_coefficients=8;
475 case PerspectiveDistortion:
476 case PerspectiveProjectionDistortion:
477 number_coefficients=9;
479 case BarrelDistortion:
480 case BarrelInverseDistortion:
481 number_coefficients=10;
484 perror(
"unknown method given");
488 coeff=(
double *) AcquireQuantumMemory(number_coefficients,
sizeof(*coeff));
489 if (coeff == (
double *) NULL)
491 (void) ThrowMagickException(exception,GetMagickModule(),
492 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
493 "GenerateCoefficients");
494 return((
double *) NULL);
498 for (i=0; i < number_coefficients; i++)
503 case AffineDistortion:
513 if ( number_arguments%cp_size != 0 ||
514 number_arguments < cp_size ) {
515 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
516 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
518 coeff=(
double *) RelinquishMagickMemory(coeff);
519 return((
double *) NULL);
522 if ( number_arguments == cp_size ) {
524 if ( cp_values == 0 ) {
527 coeff[2] = arguments[0] - arguments[2];
529 coeff[5] = arguments[1] - arguments[3];
533 for (i=0; i<number_values; i++)
534 coeff[i*3+2] = arguments[cp_values+i];
550 matrix=AcquireMagickMatrix(3UL,3UL);
551 vectors=(
double **) AcquireQuantumMemory(number_values,
553 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
555 matrix = RelinquishMagickMatrix(matrix, 3UL);
556 vectors = (
double **) RelinquishMagickMemory(vectors);
557 coeff = (
double *) RelinquishMagickMemory(coeff);
558 (void) ThrowMagickException(exception,GetMagickModule(),
559 ResourceLimitError,
"MemoryAllocationFailed",
560 "%s",
"DistortCoefficients");
561 return((
double *) NULL);
564 for (i=0; i < number_values; i++)
565 vectors[i] = &(coeff[i*3]);
567 for (i=0; i < number_arguments; i+=cp_size) {
568 terms[0] = arguments[i+cp_x];
569 terms[1] = arguments[i+cp_y];
571 LeastSquaresAddTerms(matrix,vectors,terms,
572 &(arguments[i+cp_values]),3UL,number_values);
574 if ( number_arguments == 2*cp_size ) {
579 terms[0] = arguments[cp_x]
580 - ( arguments[cp_size+cp_y] - arguments[cp_y] );
581 terms[1] = arguments[cp_y] +
582 + ( arguments[cp_size+cp_x] - arguments[cp_x] );
584 if ( cp_values == 0 ) {
588 uv2[0] = arguments[0] - arguments[5] + arguments[1];
589 uv2[1] = arguments[1] + arguments[4] - arguments[0];
590 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
594 LeastSquaresAddTerms(matrix,vectors,terms,
595 &(arguments[cp_values]),3UL,number_values);
599 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
600 matrix = RelinquishMagickMatrix(matrix, 3UL);
601 vectors = (
double **) RelinquishMagickMemory(vectors);
602 if ( status == MagickFalse ) {
603 coeff = (
double *) RelinquishMagickMemory(coeff);
604 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
605 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
606 CommandOptionToMnemonic(MagickDistortOptions, *method) );
607 return((
double *) NULL);
612 case RigidAffineDistortion:
628 if (((number_arguments % cp_size) != 0) || (number_arguments < cp_size))
630 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
631 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
632 CommandOptionToMnemonic(MagickDistortOptions,*method),2.0);
633 coeff=(
double *) RelinquishMagickMemory(coeff);
634 return((
double *) NULL);
639 matrix=AcquireMagickMatrix(4UL,4UL);
640 if (matrix == (
double **) NULL)
642 coeff=(
double *) RelinquishMagickMemory(coeff);
643 (void) ThrowMagickException(exception,GetMagickModule(),
644 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
645 CommandOptionToMnemonic(MagickDistortOptions,*method));
646 return((
double *) NULL);
651 vectors[0]=(&(coeff[0]));
652 for (i=0; i < number_arguments; i+=4)
654 terms[0]=arguments[i+0];
655 terms[1]=(-arguments[i+1]);
658 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+2]),4UL,1UL);
659 terms[0]=arguments[i+1];
660 terms[1]=arguments[i+0];
663 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+3]),4UL,1UL);
668 status=GaussJordanElimination(matrix,vectors,4UL,1UL);
669 matrix=RelinquishMagickMatrix(matrix,4UL);
670 if (status == MagickFalse)
672 coeff=(
double *) RelinquishMagickMemory(coeff);
673 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
674 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
675 CommandOptionToMnemonic(MagickDistortOptions,*method));
676 return((
double *) NULL);
683 inverse[2]=(-coeff[1]);
687 AffineArgsToCoefficients(inverse);
688 InvertAffineCoefficients(inverse,coeff);
689 *method=AffineDistortion;
692 case AffineProjectionDistortion:
708 if (number_arguments != 6) {
709 coeff = (
double *) RelinquishMagickMemory(coeff);
710 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
711 "InvalidArgument",
"%s : 'Needs 6 coeff values'",
712 CommandOptionToMnemonic(MagickDistortOptions, *method) );
713 return((
double *) NULL);
716 for(i=0; i<6UL; i++ )
717 inverse[i] = arguments[i];
718 AffineArgsToCoefficients(inverse);
719 InvertAffineCoefficients(inverse, coeff);
720 *method = AffineDistortion;
724 case ScaleRotateTranslateDistortion:
753 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
754 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
756 switch ( number_arguments ) {
758 coeff = (
double *) RelinquishMagickMemory(coeff);
759 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
760 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
761 CommandOptionToMnemonic(MagickDistortOptions, *method) );
762 return((
double *) NULL);
767 sx = sy = arguments[0];
771 x = nx = arguments[0];
772 y = ny = arguments[1];
773 switch ( number_arguments ) {
778 sx = sy = arguments[2];
787 sx = sy = arguments[2];
800 coeff = (
double *) RelinquishMagickMemory(coeff);
801 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
802 "InvalidArgument",
"%s : 'Too Many Arguments (7 or less)'",
803 CommandOptionToMnemonic(MagickDistortOptions, *method) );
804 return((
double *) NULL);
809 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
810 coeff = (
double *) RelinquishMagickMemory(coeff);
811 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
812 "InvalidArgument",
"%s : 'Zero Scale Given'",
813 CommandOptionToMnemonic(MagickDistortOptions, *method) );
814 return((
double *) NULL);
817 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
819 *method = AffineDistortion;
822 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
825 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
828 case PerspectiveDistortion:
871 if ( number_arguments%cp_size != 0 ||
872 number_arguments < cp_size*4 ) {
873 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
874 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
875 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
876 coeff=(
double *) RelinquishMagickMemory(coeff);
877 return((
double *) NULL);
880 vectors[0] = &(coeff[0]);
882 matrix = AcquireMagickMatrix(8UL,8UL);
883 if (matrix == (
double **) NULL) {
884 coeff=(
double *) RelinquishMagickMemory(coeff);
885 (void) ThrowMagickException(exception,GetMagickModule(),
886 ResourceLimitError,
"MemoryAllocationFailed",
887 "%s",
"DistortCoefficients");
888 return((
double *) NULL);
891 for (i=0; i < number_arguments; i+=4) {
892 terms[0]=arguments[i+cp_x];
893 terms[1]=arguments[i+cp_y];
898 terms[6]=-terms[0]*arguments[i+cp_u];
899 terms[7]=-terms[1]*arguments[i+cp_u];
900 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
906 terms[3]=arguments[i+cp_x];
907 terms[4]=arguments[i+cp_y];
909 terms[6]=-terms[3]*arguments[i+cp_v];
910 terms[7]=-terms[4]*arguments[i+cp_v];
911 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
915 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
916 matrix = RelinquishMagickMatrix(matrix, 8UL);
917 if ( status == MagickFalse ) {
918 coeff = (
double *) RelinquishMagickMemory(coeff);
919 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
920 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
921 CommandOptionToMnemonic(MagickDistortOptions, *method) );
922 return((
double *) NULL);
930 coeff[8] = coeff[6]*arguments[cp_x]
931 + coeff[7]*arguments[cp_y] + 1.0;
932 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
936 case PerspectiveProjectionDistortion:
941 if (number_arguments != 8) {
942 coeff = (
double *) RelinquishMagickMemory(coeff);
943 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
944 "InvalidArgument",
"%s : 'Needs 8 coefficient values'",
945 CommandOptionToMnemonic(MagickDistortOptions, *method));
946 return((
double *) NULL);
949 InvertPerspectiveCoefficients(arguments, coeff);
957 coeff[8] = coeff[6]*arguments[2]
958 + coeff[7]*arguments[5] + 1.0;
959 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
960 *method = PerspectiveDistortion;
964 case BilinearForwardDistortion:
965 case BilinearReverseDistortion:
989 if ( number_arguments%cp_size != 0 ||
990 number_arguments < cp_size*4 ) {
991 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
992 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
993 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
994 coeff=(
double *) RelinquishMagickMemory(coeff);
995 return((
double *) NULL);
998 matrix=AcquireMagickMatrix(4UL,4UL);
999 vectors=(
double **) AcquireQuantumMemory(number_values,
sizeof(*vectors));
1000 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
1002 matrix = RelinquishMagickMatrix(matrix, 4UL);
1003 vectors = (
double **) RelinquishMagickMemory(vectors);
1004 coeff = (
double *) RelinquishMagickMemory(coeff);
1005 (void) ThrowMagickException(exception,GetMagickModule(),
1006 ResourceLimitError,
"MemoryAllocationFailed",
1007 "%s",
"DistortCoefficients");
1008 return((
double *) NULL);
1011 for (i=0; i < number_values; i++)
1012 vectors[i] = &(coeff[i*4]);
1014 for (i=0; i < number_arguments; i+=cp_size) {
1015 terms[0] = arguments[i+cp_x];
1016 terms[1] = arguments[i+cp_y];
1017 terms[2] = terms[0]*terms[1];
1019 LeastSquaresAddTerms(matrix,vectors,terms,
1020 &(arguments[i+cp_values]),4UL,number_values);
1023 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
1024 matrix = RelinquishMagickMatrix(matrix, 4UL);
1025 vectors = (
double **) RelinquishMagickMemory(vectors);
1026 if ( status == MagickFalse ) {
1027 coeff = (
double *) RelinquishMagickMemory(coeff);
1028 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1029 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
1030 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1031 return((
double *) NULL);
1033 if ( *method == BilinearForwardDistortion ) {
1073 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
1074 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
1079 case QuadrilateralDistortion:
1095 case PolynomialDistortion:
1133 coeff[0] = arguments[0];
1134 coeff[1] = (double) poly_number_terms(arguments[0]);
1135 nterms = (size_t) coeff[1];
1138 matrix=AcquireMagickMatrix(nterms,nterms);
1139 vectors=(
double **) AcquireQuantumMemory(number_values,
1141 terms=(
double *) AcquireQuantumMemory(nterms,
sizeof(*terms));
1142 if ((matrix == (
double **) NULL) || (vectors == (
double **) NULL) ||
1143 (terms == (
double *) NULL))
1145 matrix = RelinquishMagickMatrix(matrix, nterms);
1146 vectors = (
double **) RelinquishMagickMemory(vectors);
1147 terms = (
double *) RelinquishMagickMemory(terms);
1148 coeff = (
double *) RelinquishMagickMemory(coeff);
1149 (void) ThrowMagickException(exception,GetMagickModule(),
1150 ResourceLimitError,
"MemoryAllocationFailed",
1151 "%s",
"DistortCoefficients");
1152 return((
double *) NULL);
1155 for (i=0; i < number_values; i++)
1156 vectors[i] = &(coeff[2+i*nterms]);
1158 for (i=1; i < number_arguments; i+=cp_size) {
1159 for (j=0; j < (ssize_t) nterms; j++)
1160 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1161 LeastSquaresAddTerms(matrix,vectors,terms,
1162 &(arguments[i+cp_values]),nterms,number_values);
1164 terms = (
double *) RelinquishMagickMemory(terms);
1166 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1167 matrix = RelinquishMagickMatrix(matrix, nterms);
1168 vectors = (
double **) RelinquishMagickMemory(vectors);
1169 if ( status == MagickFalse ) {
1170 coeff = (
double *) RelinquishMagickMemory(coeff);
1171 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1172 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
1173 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1174 return((
double *) NULL);
1214 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1215 coeff = (
double *) RelinquishMagickMemory(coeff);
1216 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1217 "InvalidArgument",
"%s : 'Arc Angle Too Small'",
1218 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1219 return((
double *) NULL);
1221 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1222 coeff = (
double *) RelinquishMagickMemory(coeff);
1223 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1224 "InvalidArgument",
"%s : 'Outer Radius Too Small'",
1225 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1226 return((
double *) NULL);
1228 coeff[0] = -MagickPI2;
1229 if ( number_arguments >= 1 )
1230 coeff[1] = DegreesToRadians(arguments[0]);
1232 coeff[1] = MagickPI2;
1233 if ( number_arguments >= 2 )
1234 coeff[0] += DegreesToRadians(arguments[1]);
1235 coeff[0] /= Magick2PI;
1236 coeff[0] -= MagickRound(coeff[0]);
1237 coeff[0] *= Magick2PI;
1238 coeff[3] = (double)image->rows-1;
1239 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1240 if ( number_arguments >= 3 ) {
1241 if ( number_arguments >= 4 )
1242 coeff[3] = arguments[2] - arguments[3];
1244 coeff[3] *= arguments[2]/coeff[2];
1245 coeff[2] = arguments[2];
1247 coeff[4] = ((double)image->columns-1.0)/2.0;
1251 case PolarDistortion:
1252 case DePolarDistortion:
1264 if ( number_arguments == 3
1265 || ( number_arguments > 6 && *method == PolarDistortion )
1266 || number_arguments > 8 ) {
1267 (void) ThrowMagickException(exception,GetMagickModule(),
1268 OptionError,
"InvalidArgument",
"%s : number of arguments",
1269 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1270 coeff=(
double *) RelinquishMagickMemory(coeff);
1271 return((
double *) NULL);
1274 if ( number_arguments >= 1 )
1275 coeff[0] = arguments[0];
1279 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1281 if ( number_arguments >= 4 ) {
1282 coeff[2] = arguments[2];
1283 coeff[3] = arguments[3];
1286 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1287 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1290 coeff[4] = -MagickPI;
1291 if ( number_arguments >= 5 )
1292 coeff[4] = DegreesToRadians(arguments[4]);
1293 coeff[5] = coeff[4];
1294 if ( number_arguments >= 6 )
1295 coeff[5] = DegreesToRadians(arguments[5]);
1296 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1297 coeff[5] += Magick2PI;
1299 if ( coeff[0] < MagickEpsilon ) {
1301 if ( fabs(coeff[0]) < MagickEpsilon ) {
1302 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1303 fabs(coeff[3]-image->page.y));
1304 coeff[0]=MagickMin(coeff[0],
1305 fabs(coeff[2]-image->page.x-image->columns));
1306 coeff[0]=MagickMin(coeff[0],
1307 fabs(coeff[3]-image->page.y-image->rows));
1310 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1312 rx = coeff[2]-image->page.x;
1313 ry = coeff[3]-image->page.y;
1314 coeff[0] = rx*rx+ry*ry;
1315 ry = coeff[3]-image->page.y-image->rows;
1316 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1317 rx = coeff[2]-image->page.x-image->columns;
1318 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1319 ry = coeff[3]-image->page.y;
1320 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1321 coeff[0] = sqrt(coeff[0]);
1325 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1326 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1327 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1328 "InvalidArgument",
"%s : Invalid Radius",
1329 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1330 coeff=(
double *) RelinquishMagickMemory(coeff);
1331 return((
double *) NULL);
1334 if ( *method == PolarDistortion ) {
1335 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1336 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1339 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1340 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1344 case Cylinder2PlaneDistortion:
1345 case Plane2CylinderDistortion:
1369 if (number_arguments < 1) {
1370 coeff = (
double *) RelinquishMagickMemory(coeff);
1371 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1372 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
1373 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1374 return((
double *) NULL);
1376 if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
1377 coeff=(
double *) RelinquishMagickMemory(coeff);
1378 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1379 "InvalidArgument",
"%s : Invalid FOV Angle",
1380 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1381 return((
double *) NULL);
1383 coeff[0] = DegreesToRadians(arguments[0]);
1384 if ( *method == Cylinder2PlaneDistortion )
1388 coeff[1] = (double) image->columns/coeff[0];
1391 coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );
1393 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1394 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1395 coeff[4] = coeff[2];
1396 coeff[5] = coeff[3];
1399 case BarrelDistortion:
1400 case BarrelInverseDistortion:
1421 rscale = 2.0/MagickMin((
double) image->columns,(
double) image->rows);
1424 if ( (number_arguments < 3) || (number_arguments == 7) ||
1425 (number_arguments == 9) || (number_arguments > 10) )
1427 coeff=(
double *) RelinquishMagickMemory(coeff);
1428 (void) ThrowMagickException(exception,GetMagickModule(),
1429 OptionError,
"InvalidArgument",
"%s : number of arguments",
1430 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1431 return((
double *) NULL);
1434 coeff[0] = arguments[0];
1435 coeff[1] = arguments[1];
1436 coeff[2] = arguments[2];
1437 if ((number_arguments == 3) || (number_arguments == 5) )
1438 coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1440 coeff[3] = arguments[3];
1442 coeff[0] *= pow(rscale,3.0);
1443 coeff[1] *= rscale*rscale;
1446 if ( number_arguments >= 8 ) {
1447 coeff[4] = arguments[4] * pow(rscale,3.0);
1448 coeff[5] = arguments[5] * rscale*rscale;
1449 coeff[6] = arguments[6] * rscale;
1450 coeff[7] = arguments[7];
1453 coeff[4] = coeff[0];
1454 coeff[5] = coeff[1];
1455 coeff[6] = coeff[2];
1456 coeff[7] = coeff[3];
1459 if ( number_arguments == 5 ) {
1460 coeff[8] = arguments[3];
1461 coeff[9] = arguments[4];
1463 else if ( number_arguments == 6 ) {
1464 coeff[8] = arguments[4];
1465 coeff[9] = arguments[5];
1467 else if ( number_arguments == 10 ) {
1468 coeff[8] = arguments[8];
1469 coeff[9] = arguments[9];
1473 coeff[8] = (double)image->columns/2.0 + image->page.x;
1474 coeff[9] = (double)image->rows/2.0 + image->page.y;
1478 case ShepardsDistortion:
1485 if ( number_arguments%cp_size != 0 ||
1486 number_arguments < cp_size ) {
1487 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1488 "InvalidArgument",
"%s : 'requires CP's (4 numbers each)'",
1489 CommandOptionToMnemonic(MagickDistortOptions, *method));
1490 coeff=(
double *) RelinquishMagickMemory(coeff);
1491 return((
double *) NULL);
1494 {
const char *artifact=GetImageArtifact(image,
"shepards:power");
1495 if ( artifact != (
const char *) NULL ) {
1496 coeff[0]=StringToDouble(artifact,(
char **) NULL) / 2.0;
1497 if ( coeff[0] < MagickEpsilon ) {
1498 (void) ThrowMagickException(exception,GetMagickModule(),
1499 OptionError,
"InvalidArgument",
"%s",
"-define shepards:power" );
1500 coeff=(
double *) RelinquishMagickMemory(coeff);
1501 return((
double *) NULL);
1513 perror(
"no method handler");
1514 return((
double *) NULL);
1552MagickExport
Image *DistortResizeImage(
const Image *image,
const size_t columns,
1555#define DistortResizeImageTag "Distort/Image"
1573 assert(image != (
const Image *) NULL);
1574 assert(image->signature == MagickCoreSignature);
1576 assert(exception->signature == MagickCoreSignature);
1577 if (IsEventLogging() != MagickFalse)
1578 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1579 if ((columns == 0) || (rows == 0))
1580 return((
Image *) NULL);
1583 (void) memset(distort_args,0,
sizeof(distort_args));
1584 distort_args[4]=(double) image->columns;
1585 distort_args[6]=(double) columns;
1586 distort_args[9]=(double) image->rows;
1587 distort_args[11]=(double) rows;
1589 vp_save=GetImageVirtualPixelMethod(image);
1591 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1592 if (tmp_image == (
Image *) NULL)
1593 return((
Image *) NULL);
1594 (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
1597 if ((image->alpha_trait & BlendPixelTrait) == 0)
1602 (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel,exception);
1603 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1604 MagickTrue,exception),
1605 tmp_image=DestroyImage(tmp_image);
1606 if (resize_image == (
Image *) NULL)
1607 return((
Image *) NULL);
1608 (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1622 (void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
1623 (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
1624 resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1625 MagickTrue,exception),
1626 tmp_image=DestroyImage(tmp_image);
1627 if (resize_alpha == (
Image *) NULL)
1628 return((
Image *) NULL);
1631 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1632 if (tmp_image == (
Image *) NULL)
1633 return((
Image *) NULL);
1634 (void) SetImageVirtualPixelMethod(tmp_image,
1635 TransparentVirtualPixelMethod,exception);
1636 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1637 MagickTrue,exception),
1638 tmp_image=DestroyImage(tmp_image);
1639 if (resize_image == (
Image *) NULL)
1641 resize_alpha=DestroyImage(resize_alpha);
1642 return((
Image *) NULL);
1645 (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1646 (void) SetImageAlphaChannel(resize_alpha,OffAlphaChannel,exception);
1647 (void) CompositeImage(resize_image,resize_alpha,CopyAlphaCompositeOp,
1648 MagickTrue,0,0,exception);
1649 resize_alpha=DestroyImage(resize_alpha);
1650 resize_image->alpha_trait=image->alpha_trait;
1651 resize_image->compose=image->compose;
1653 (void) SetImageVirtualPixelMethod(resize_image,vp_save,exception);
1658 crop_area.width=columns;
1659 crop_area.height=rows;
1663 tmp_image=resize_image;
1664 resize_image=CropImage(tmp_image,&crop_area,exception);
1665 tmp_image=DestroyImage(tmp_image);
1666 if (resize_image != (
Image *) NULL)
1668 resize_image->page.width=0;
1669 resize_image->page.height=0;
1671 return(resize_image);
1762MagickExport
Image *DistortImage(
const Image *image, DistortMethod method,
1763 const size_t number_arguments,
const double *arguments,
1766#define DistortImageTag "Distort/Image"
1784 assert(image != (
Image *) NULL);
1785 assert(image->signature == MagickCoreSignature);
1787 assert(exception->signature == MagickCoreSignature);
1788 if (IsEventLogging() != MagickFalse)
1789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1793 if ( method == ResizeDistortion )
1795 if ( number_arguments != 2 )
1797 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1798 "InvalidArgument",
"%s : '%s'",
"Resize",
1799 "Invalid number of args: 2 only");
1800 return((
Image *) NULL);
1802 distort_image=DistortResizeImage(image,(
size_t)arguments[0],
1803 (
size_t)arguments[1], exception);
1804 return(distort_image);
1814 coeff = GenerateCoefficients(image, &method, number_arguments,
1815 arguments, 0, exception);
1816 if ( coeff == (
double *) NULL )
1817 return((
Image *) NULL);
1825 geometry.width=image->columns;
1826 geometry.height=image->rows;
1830 if ( method == ArcDistortion ) {
1831 bestfit = MagickTrue;
1840 fix_bounds = MagickTrue;
1842 s.x=s.y=min.x=max.x=min.y=max.y=0.0;
1845#define InitalBounds(p) \
1848 min.x = max.x = p.x; \
1849 min.y = max.y = p.y; \
1851#define ExpandBounds(p) \
1854 min.x = MagickMin(min.x,p.x); \
1855 max.x = MagickMax(max.x,p.x); \
1856 min.y = MagickMin(min.y,p.y); \
1857 max.y = MagickMax(max.y,p.y); \
1862 case AffineDistortion:
1863 case RigidAffineDistortion:
1864 {
double inverse[6];
1865 InvertAffineCoefficients(coeff, inverse);
1866 s.x = (double) image->page.x;
1867 s.y = (double) image->page.y;
1868 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1869 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1871 s.x = (double) image->page.x+image->columns;
1872 s.y = (double) image->page.y;
1873 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1874 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1876 s.x = (double) image->page.x;
1877 s.y = (double) image->page.y+image->rows;
1878 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1879 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1881 s.x = (double) image->page.x+image->columns;
1882 s.y = (double) image->page.y+image->rows;
1883 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1884 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1888 case PerspectiveDistortion:
1889 {
double inverse[8], scale;
1890 InvertPerspectiveCoefficients(coeff, inverse);
1891 s.x = (double) image->page.x;
1892 s.y = (double) image->page.y;
1893 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1894 scale=PerceptibleReciprocal(scale);
1895 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1896 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1898 s.x = (double) image->page.x+image->columns;
1899 s.y = (double) image->page.y;
1900 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1901 scale=PerceptibleReciprocal(scale);
1902 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1903 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1905 s.x = (double) image->page.x;
1906 s.y = (double) image->page.y+image->rows;
1907 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1908 scale=PerceptibleReciprocal(scale);
1909 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1910 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1912 s.x = (double) image->page.x+image->columns;
1913 s.y = (double) image->page.y+image->rows;
1914 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1915 scale=PerceptibleReciprocal(scale);
1916 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1917 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1924 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1928 d.x = (coeff[2]-coeff[3])*ca;
1929 d.y = (coeff[2]-coeff[3])*sa;
1931 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1935 d.x = (coeff[2]-coeff[3])*ca;
1936 d.y = (coeff[2]-coeff[3])*sa;
1939 for( a=(
double) (ceil((
double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
1940 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1941 ca = cos(a); sa = sin(a);
1951 coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
1952 coeff[3] = (double)image->rows/coeff[3];
1955 case PolarDistortion:
1957 if (number_arguments < 2)
1958 coeff[2] = coeff[3] = 0.0;
1959 min.x = coeff[2]-coeff[0];
1960 max.x = coeff[2]+coeff[0];
1961 min.y = coeff[3]-coeff[0];
1962 max.y = coeff[3]+coeff[0];
1964 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1967 case DePolarDistortion:
1971 fix_bounds = MagickFalse;
1972 geometry.x = geometry.y = 0;
1973 geometry.height = (size_t) ceil(coeff[0]-coeff[1]);
1974 geometry.width = (size_t) ceil((coeff[0]-coeff[1])*
1975 (coeff[5]-coeff[4])*0.5);
1977 coeff[6]=(coeff[5]-coeff[4])*PerceptibleReciprocal(geometry.width);
1978 coeff[7]=(coeff[0]-coeff[1])*PerceptibleReciprocal(geometry.height);
1981 case Cylinder2PlaneDistortion:
1986 geometry.x = geometry.y = 0;
1987 geometry.width = (size_t) ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) );
1988 geometry.height = (size_t) ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) );
1990 coeff[4] = (double) geometry.width/2.0;
1991 coeff[5] = (double) geometry.height/2.0;
1992 fix_bounds = MagickFalse;
1995 case Plane2CylinderDistortion:
1999 geometry.x = geometry.y = 0;
2000 geometry.width = (size_t) ceil(coeff[0]*coeff[1]);
2001 geometry.height = (size_t) (2*coeff[3]);
2003 coeff[4] = (double) geometry.width/2.0;
2004 coeff[5] = (double) geometry.height/2.0;
2005 fix_bounds = MagickFalse;
2008 case ShepardsDistortion:
2009 case BilinearForwardDistortion:
2010 case BilinearReverseDistortion:
2012 case QuadrilateralDistortion:
2014 case PolynomialDistortion:
2015 case BarrelDistortion:
2016 case BarrelInverseDistortion:
2019 bestfit = MagickFalse;
2020 fix_bounds = MagickFalse;
2029 geometry.x = (ssize_t) floor(min.x-0.5);
2030 geometry.y = (ssize_t) floor(min.y-0.5);
2031 geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
2032 geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
2041 {
const char *artifact=GetImageArtifact(image,
"distort:viewport");
2042 viewport_given = MagickFalse;
2043 if ( artifact != (
const char *) NULL ) {
2044 MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
2046 (void) ThrowMagickException(exception,GetMagickModule(),
2047 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
2048 "distort:viewport",artifact);
2050 viewport_given = MagickTrue;
2055 if (IsStringTrue(GetImageArtifact(image,
"verbose")) != MagickFalse) {
2058 char image_gen[MagickPathExtent];
2062 if ( bestfit || viewport_given ) {
2063 (void) FormatLocaleString(image_gen,MagickPathExtent,
2064 " -size %.20gx%.20g -page %+.20g%+.20g xc: +insert \\\n",
2065 (
double) geometry.width,(double) geometry.height,(
double) geometry.x,
2066 (double) geometry.y);
2067 lookup=
"v.p{xx-v.page.x-0.5,yy-v.page.y-0.5}";
2070 image_gen[0] =
'\0';
2071 lookup =
"p{xx-page.x-0.5,yy-page.y-0.5}";
2076 case AffineDistortion:
2077 case RigidAffineDistortion:
2082 inverse=(
double *) AcquireQuantumMemory(6,
sizeof(*inverse));
2083 if (inverse == (
double *) NULL)
2085 coeff=(
double *) RelinquishMagickMemory(coeff);
2086 (void) ThrowMagickException(exception,GetMagickModule(),
2087 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
"DistortImages");
2088 return((
Image *) NULL);
2090 InvertAffineCoefficients(coeff, inverse);
2091 CoefficientsToAffineArgs(inverse);
2092 (void) FormatLocaleFile(stderr,
"Affine projection:\n");
2093 (void) FormatLocaleFile(stderr,
2094 " -distort AffineProjection \\\n '");
2095 for (i=0; i < 5; i++)
2096 (
void) FormatLocaleFile(stderr,
"%.*g,",GetMagickPrecision(),
2098 (void) FormatLocaleFile(stderr,
"%.*g'\n",GetMagickPrecision(),
2100 (void) FormatLocaleFile(stderr,
2101 "Equivalent scale, rotation(deg), translation:\n");
2102 (void) FormatLocaleFile(stderr,
" %.*g,%.*g,%.*g,%.*g\n",
2103 GetMagickPrecision(),sqrt(inverse[0]*inverse[0]+
2104 inverse[1]*inverse[1]),GetMagickPrecision(),
2105 RadiansToDegrees(atan2(inverse[1],inverse[0])),
2106 GetMagickPrecision(),inverse[4],GetMagickPrecision(),inverse[5]);
2107 inverse=(
double *) RelinquishMagickMemory(inverse);
2108 (void) FormatLocaleFile(stderr,
"Affine distort, FX equivalent:\n");
2109 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2110 (void) FormatLocaleFile(stderr,
2111 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2112 (void) FormatLocaleFile(stderr,
" xx=%+.*g*ii %+.*g*jj %+.*g;\n",
2113 GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2114 GetMagickPrecision(),coeff[2]);
2115 (void) FormatLocaleFile(stderr,
" yy=%+.*g*ii %+.*g*jj %+.*g;\n",
2116 GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2117 GetMagickPrecision(),coeff[5]);
2118 (void) FormatLocaleFile(stderr,
" %s' \\\n",lookup);
2121 case PerspectiveDistortion:
2126 inverse=(
double *) AcquireQuantumMemory(8,
sizeof(*inverse));
2127 if (inverse == (
double *) NULL)
2129 coeff=(
double *) RelinquishMagickMemory(coeff);
2130 (void) ThrowMagickException(exception,GetMagickModule(),
2131 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
2132 "DistortCoefficients");
2133 return((
Image *) NULL);
2135 InvertPerspectiveCoefficients(coeff, inverse);
2136 (void) FormatLocaleFile(stderr,
"Perspective Projection:\n");
2137 (void) FormatLocaleFile(stderr,
2138 " -distort PerspectiveProjection \\\n '");
2139 for (i=0; i < 4; i++)
2140 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2142 (void) FormatLocaleFile(stderr,
"\n ");
2144 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2146 (void) FormatLocaleFile(stderr,
"%.*g'\n",GetMagickPrecision(),
2148 inverse=(
double *) RelinquishMagickMemory(inverse);
2149 (void) FormatLocaleFile(stderr,
"Perspective Distort, FX Equivalent:\n");
2150 (void) FormatLocaleFile(stderr,
"%.1024s",image_gen);
2151 (void) FormatLocaleFile(stderr,
2152 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2153 (void) FormatLocaleFile(stderr,
" rr=%+.*g*ii %+.*g*jj + 1;\n",
2154 GetMagickPrecision(),coeff[6],GetMagickPrecision(),coeff[7]);
2155 (void) FormatLocaleFile(stderr,
2156 " xx=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2157 GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2158 GetMagickPrecision(),coeff[2]);
2159 (void) FormatLocaleFile(stderr,
2160 " yy=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2161 GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2162 GetMagickPrecision(),coeff[5]);
2163 (void) FormatLocaleFile(stderr,
" rr%s0 ? %s : blue' \\\n",
2164 coeff[8] < 0.0 ?
"<" :
">", lookup);
2167 case BilinearForwardDistortion:
2169 (void) FormatLocaleFile(stderr,
"BilinearForward Mapping Equations:\n");
2170 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2171 (void) FormatLocaleFile(stderr,
" i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2172 coeff[0],coeff[1],coeff[2],coeff[3]);
2173 (void) FormatLocaleFile(stderr,
" j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2174 coeff[4],coeff[5],coeff[6],coeff[7]);
2177 (void) FormatLocaleFile(stderr,
" c8 = %+lf c9 = 2*a = %+lf;\n",
2178 coeff[8], coeff[9]);
2180 (void) FormatLocaleFile(stderr,
2181 "BilinearForward Distort, FX Equivalent:\n");
2182 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2183 (void) FormatLocaleFile(stderr,
2184 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",0.5-coeff[3],0.5-
2186 (void) FormatLocaleFile(stderr,
" bb=%lf*ii %+lf*jj %+lf;\n",
2187 coeff[6], -coeff[2], coeff[8]);
2191 (void) FormatLocaleFile(stderr,
2192 " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",-2*coeff[9],coeff[4],
2194 (void) FormatLocaleFile(stderr,
2195 " yy=( -bb + sqrt(rt) ) / %lf;\n",coeff[9]);
2198 (
void) FormatLocaleFile(stderr,
" yy=(%lf*ii%+lf*jj)/bb;\n",
2199 -coeff[4],coeff[0]);
2200 (void) FormatLocaleFile(stderr,
2201 " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",-coeff[1],coeff[0],
2203 if ( coeff[9] != 0 )
2204 (void) FormatLocaleFile(stderr,
" (rt < 0 ) ? red : %s'\n",
2207 (
void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2210 case BilinearReverseDistortion:
2213 (void) FormatLocaleFile(stderr,
"Polynomial Projection Distort:\n");
2214 (void) FormatLocaleFile(stderr,
" -distort PolynomialProjection \\\n");
2215 (void) FormatLocaleFile(stderr,
" '1.5, %lf, %lf, %lf, %lf,\n",
2216 coeff[3], coeff[0], coeff[1], coeff[2]);
2217 (void) FormatLocaleFile(stderr,
" %lf, %lf, %lf, %lf'\n",
2218 coeff[7], coeff[4], coeff[5], coeff[6]);
2220 (void) FormatLocaleFile(stderr,
2221 "BilinearReverse Distort, FX Equivalent:\n");
2222 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2223 (void) FormatLocaleFile(stderr,
2224 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2225 (void) FormatLocaleFile(stderr,
2226 " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[0],coeff[1],
2227 coeff[2], coeff[3]);
2228 (void) FormatLocaleFile(stderr,
2229 " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[4],coeff[5],
2230 coeff[6], coeff[7]);
2231 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2234 case PolynomialDistortion:
2236 size_t nterms = (size_t) coeff[1];
2237 (void) FormatLocaleFile(stderr,
2238 "Polynomial (order %lg, terms %lu), FX Equivalent\n",coeff[0],
2239 (
unsigned long) nterms);
2240 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2241 (void) FormatLocaleFile(stderr,
2242 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2243 (void) FormatLocaleFile(stderr,
" xx =");
2244 for (i=0; i < (ssize_t) nterms; i++)
2246 if ((i != 0) && (i%4 == 0))
2247 (void) FormatLocaleFile(stderr,
"\n ");
2248 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i],
2251 (void) FormatLocaleFile(stderr,
";\n yy =");
2252 for (i=0; i < (ssize_t) nterms; i++)
2254 if ((i != 0) && (i%4 == 0))
2255 (void) FormatLocaleFile(stderr,
"\n ");
2256 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i+(
int) nterms],
2259 (void) FormatLocaleFile(stderr,
";\n %s' \\\n", lookup);
2264 (void) FormatLocaleFile(stderr,
"Arc Distort, Internal Coefficients:\n");
2265 for (i=0; i < 5; i++)
2266 (
void) FormatLocaleFile(stderr,
2267 " c%.20g = %+lf\n",(
double) i,coeff[i]);
2268 (void) FormatLocaleFile(stderr,
"Arc Distort, FX Equivalent:\n");
2269 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2270 (void) FormatLocaleFile(stderr,
" -fx 'ii=i+page.x; jj=j+page.y;\n");
2271 (void) FormatLocaleFile(stderr,
" xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
2273 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2274 (void) FormatLocaleFile(stderr,
" xx=xx*%lf %+lf;\n",coeff[1],
2276 (void) FormatLocaleFile(stderr,
2277 " yy=(%lf - hypot(ii,jj)) * %lf;\n",coeff[2],coeff[3]);
2278 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2281 case PolarDistortion:
2283 (void) FormatLocaleFile(stderr,
"Polar Distort, Internal Coefficients\n");
2284 for (i=0; i < 8; i++)
2285 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2287 (void) FormatLocaleFile(stderr,
"Polar Distort, FX Equivalent:\n");
2288 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2289 (void) FormatLocaleFile(stderr,
2290 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",-coeff[2],-coeff[3]);
2291 (void) FormatLocaleFile(stderr,
" xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
2292 -(coeff[4]+coeff[5])/2 );
2293 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2294 (void) FormatLocaleFile(stderr,
" xx=xx*2*pi*%lf + v.w/2;\n",
2296 (void) FormatLocaleFile(stderr,
" yy=(hypot(ii,jj)%+lf)*%lf;\n",
2297 -coeff[1],coeff[7] );
2298 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2301 case DePolarDistortion:
2303 (void) FormatLocaleFile(stderr,
2304 "DePolar Distort, Internal Coefficients\n");
2305 for (i=0; i < 8; i++)
2306 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2308 (void) FormatLocaleFile(stderr,
"DePolar Distort, FX Equivalent:\n");
2309 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2310 (void) FormatLocaleFile(stderr,
" -fx 'aa=(i+.5)*%lf %+lf;\n",
2311 coeff[6],+coeff[4]);
2312 (void) FormatLocaleFile(stderr,
" rr=(j+.5)*%lf %+lf;\n",
2313 coeff[7],+coeff[1]);
2314 (void) FormatLocaleFile(stderr,
" xx=rr*sin(aa) %+lf;\n",
2316 (void) FormatLocaleFile(stderr,
" yy=rr*cos(aa) %+lf;\n",
2318 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2321 case Cylinder2PlaneDistortion:
2323 (void) FormatLocaleFile(stderr,
2324 "Cylinder to Plane Distort, Internal Coefficients\n");
2325 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2326 (void) FormatLocaleFile(stderr,
2327 "Cylinder to Plane Distort, FX Equivalent:\n");
2328 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2329 (void) FormatLocaleFile(stderr,
2330 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2332 (void) FormatLocaleFile(stderr,
" aa=atan(ii/%+lf);\n",coeff[1]);
2333 (void) FormatLocaleFile(stderr,
" xx=%lf*aa%+lf;\n",
2335 (void) FormatLocaleFile(stderr,
" yy=jj*cos(aa)%+lf;\n",coeff[3]);
2336 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2339 case Plane2CylinderDistortion:
2341 (void) FormatLocaleFile(stderr,
2342 "Plane to Cylinder Distort, Internal Coefficients\n");
2343 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2344 (void) FormatLocaleFile(stderr,
2345 "Plane to Cylinder Distort, FX Equivalent:\n");
2346 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2347 (void) FormatLocaleFile(stderr,
2348 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2350 (void) FormatLocaleFile(stderr,
" ii=ii/%+lf;\n",coeff[1]);
2351 (void) FormatLocaleFile(stderr,
" xx=%lf*tan(ii)%+lf;\n",coeff[1],
2353 (void) FormatLocaleFile(stderr,
" yy=jj/cos(ii)%+lf;\n",coeff[3]);
2354 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2357 case BarrelDistortion:
2358 case BarrelInverseDistortion:
2369 xc=((double)image->columns-1.0)/2.0+image->page.x;
2370 yc=((double)image->rows-1.0)/2.0+image->page.y;
2371 (void) FormatLocaleFile(stderr,
"Barrel%s Distort, FX Equivalent:\n",
2372 method == BarrelDistortion ?
"" :
"Inv");
2373 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2374 if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
2375 (
void) FormatLocaleFile(stderr,
" -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
2377 (
void) FormatLocaleFile(stderr,
" -fx 'xc=%lf; yc=%lf;\n",coeff[8]-
2379 (void) FormatLocaleFile(stderr,
2380 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
2381 (void) FormatLocaleFile(stderr,
2382 " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2383 method == BarrelDistortion ?
"*" :
"/",coeff[0],coeff[1],coeff[2],
2385 (void) FormatLocaleFile(stderr,
2386 " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2387 method == BarrelDistortion ?
"*" :
"/",coeff[4],coeff[5],coeff[6],
2389 (void) FormatLocaleFile(stderr,
" p{ii+xc,jj+yc}' \\\n");
2400 {
const char *artifact;
2401 artifact=GetImageArtifact(image,
"distort:scale");
2402 output_scaling = 1.0;
2403 if (artifact != (
const char *) NULL) {
2404 output_scaling = fabs(StringToDouble(artifact,(
char **) NULL));
2405 geometry.width=(size_t) (output_scaling*geometry.width+0.5);
2406 geometry.height=(size_t) (output_scaling*geometry.height+0.5);
2407 geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
2408 geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
2409 if ( output_scaling < 0.1 ) {
2410 coeff = (
double *) RelinquishMagickMemory(coeff);
2411 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2412 "InvalidArgument",
"%s",
"-set option:distort:scale" );
2413 return((
Image *) NULL);
2415 output_scaling = 1/output_scaling;
2418#define ScaleFilter(F,A,B,C,D) \
2419 ScaleResampleFilter( (F), \
2420 output_scaling*(A), output_scaling*(B), \
2421 output_scaling*(C), output_scaling*(D) )
2426 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
2428 if (distort_image == (
Image *) NULL)
2430 coeff=(
double *) RelinquishMagickMemory(coeff);
2431 return((
Image *) NULL);
2434 if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
2436 coeff=(
double *) RelinquishMagickMemory(coeff);
2437 distort_image=DestroyImage(distort_image);
2438 return((
Image *) NULL);
2440 if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
2441 (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
2442 (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
2443 if (distort_image->background_color.alpha_trait != UndefinedPixelTrait)
2444 distort_image->alpha_trait=BlendPixelTrait;
2445 distort_image->page.x=geometry.x;
2446 distort_image->page.y=geometry.y;
2447 ConformPixelInfo(distort_image,&distort_image->matte_color,&invalid,
2466 **magick_restrict resample_filter;
2473 GetPixelInfo(distort_image,&zero);
2474 resample_filter=AcquireResampleFilterTLS(image,UndefinedVirtualPixelMethod,
2475 MagickFalse,exception);
2476 distort_view=AcquireAuthenticCacheView(distort_image,exception);
2477#if defined(MAGICKCORE_OPENMP_SUPPORT)
2478 #pragma omp parallel for schedule(static) shared(progress,status) \
2479 magick_number_threads(image,distort_image,distort_image->rows,1)
2481 for (j=0; j < (ssize_t) distort_image->rows; j++)
2484 id = GetOpenMPThreadId();
2505 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
2507 if (q == (Quantum *) NULL)
2519 case AffineDistortion:
2520 case RigidAffineDistortion:
2521 ScaleFilter( resample_filter[
id],
2523 coeff[3], coeff[4] );
2536 for (i=0; i < (ssize_t) distort_image->columns; i++)
2539 d.x = (double) (geometry.x+i+0.5)*output_scaling;
2540 d.y = (double) (geometry.y+j+0.5)*output_scaling;
2544 case AffineDistortion:
2545 case RigidAffineDistortion:
2547 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2548 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2552 case PerspectiveDistortion:
2555 p,n,r,abs_r,abs_c6,abs_c7,scale;
2557 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2558 n=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2559 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2561 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2564 abs_c6 = fabs(coeff[6]);
2565 abs_c7 = fabs(coeff[7]);
2566 if ( abs_c6 > abs_c7 ) {
2567 if ( abs_r < abs_c6*output_scaling )
2568 validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
2570 else if ( abs_r < abs_c7*output_scaling )
2571 validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
2573 if ( validity > 0.0 ) {
2580 ScaleFilter( resample_filter[
id],
2581 (r*coeff[0] - p*coeff[6])*scale,
2582 (r*coeff[1] - p*coeff[7])*scale,
2583 (r*coeff[3] - n*coeff[6])*scale,
2584 (r*coeff[4] - n*coeff[7])*scale );
2588 case BilinearReverseDistortion:
2591 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2592 s.y=coeff[4]*d.x+coeff[5]*d.y
2593 +coeff[6]*d.x*d.y+coeff[7];
2595 ScaleFilter( resample_filter[
id],
2596 coeff[0] + coeff[2]*d.y,
2597 coeff[1] + coeff[2]*d.x,
2598 coeff[4] + coeff[6]*d.y,
2599 coeff[5] + coeff[6]*d.x );
2602 case BilinearForwardDistortion:
2607 d.x -= coeff[3]; d.y -= coeff[7];
2608 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2609 c = coeff[4]*d.x - coeff[0]*d.y;
2614 if ( fabs(coeff[9]) < MagickEpsilon )
2617 c = b*b - 2*coeff[9]*c;
2621 s.y = ( -b + sqrt(c) )/coeff[9];
2623 if ( validity > 0.0 )
2624 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2634 case BilinearDistortion:
2639 case PolynomialDistortion:
2646 nterms=(ssize_t)coeff[1];
2651 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2652 for(k=0; k < nterms; k++) {
2653 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2654 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2655 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2656 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2657 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2658 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2660 ScaleFilter( resample_filter[
id], du.x,du.y,dv.x,dv.y );
2666 s.x = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
2667 s.x -= MagickRound(s.x);
2668 s.y = hypot(d.x,d.y);
2675 if ( s.y > MagickEpsilon )
2676 ScaleFilter( resample_filter[
id],
2677 (
double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
2679 ScaleFilter( resample_filter[
id],
2680 distort_image->columns*2, 0, 0, coeff[3] );
2683 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2684 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2687 case PolarDistortion:
2691 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2693 s.x -= MagickRound(s.x);
2695 s.y = hypot(d.x,d.y);
2700 if ( s.y > MagickEpsilon )
2701 ScaleFilter( resample_filter[
id],
2702 (
double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
2704 ScaleFilter( resample_filter[
id],
2705 distort_image->columns*2, 0, 0, coeff[7] );
2708 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2709 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2712 case DePolarDistortion:
2715 d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
2716 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2717 s.x = d.y*sin(d.x) + coeff[2];
2718 s.y = d.y*cos(d.x) + coeff[3];
2722 case Cylinder2PlaneDistortion:
2726 d.x -= coeff[4]; d.y -= coeff[5];
2733 ScaleFilter( resample_filter[
id],
2734 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2736if ( i == 0 && j == 0 ) {
2737 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2738 fprintf(stderr,
"phi = %lf\n", (
double)(ax * 180.0/MagickPI) );
2739 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2740 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2744 s.x += coeff[2]; s.y += coeff[3];
2747 case Plane2CylinderDistortion:
2750 d.x -= coeff[4]; d.y -= coeff[5];
2754 validity = (double) (coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5;
2756 if ( validity > 0.0 ) {
2764 ScaleFilter( resample_filter[
id],
2765 cx*cx, 0.0, s.y*cx/coeff[1], cx );
2768if ( d.x == 0.5 && d.y == 0.5 ) {
2769 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2770 fprintf(stderr,
"radius = %lf phi = %lf validity = %lf\n",
2771 coeff[1], (
double)(d.x * 180.0/MagickPI), validity );
2772 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2773 cx*cx, 0.0, s.y*cx/coeff[1], cx);
2778 s.x += coeff[2]; s.y += coeff[3];
2781 case BarrelDistortion:
2782 case BarrelInverseDistortion:
2784 double r,fx,fy,gx,gy;
2788 r = sqrt(d.x*d.x+d.y*d.y);
2789 if ( r > MagickEpsilon ) {
2790 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2791 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2792 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2793 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2795 if ( method == BarrelInverseDistortion ) {
2796 fx = 1/fx; fy = 1/fy;
2797 gx *= -fx*fx; gy *= -fy*fy;
2800 s.x = d.x*fx + coeff[8];
2801 s.y = d.y*fy + coeff[9];
2802 ScaleFilter( resample_filter[
id],
2803 gx*d.x*d.x + fx, gx*d.x*d.y,
2804 gy*d.x*d.y, gy*d.y*d.y + fy );
2813 if ( method == BarrelDistortion )
2814 ScaleFilter( resample_filter[
id],
2815 coeff[3], 0, 0, coeff[7] );
2818 ScaleFilter( resample_filter[
id],
2819 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2823 case ShepardsDistortion:
2839 denominator = s.x = s.y = 0;
2840 for(k=0; k<number_arguments; k+=4) {
2842 ((double)d.x-arguments[k+2])*((double)d.x-arguments[k+2])
2843 + ((double)d.y-arguments[k+3])*((double)d.y-arguments[k+3]);
2844 weight = pow(weight,coeff[0]);
2845 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
2847 s.x += (arguments[ k ]-arguments[k+2])*weight;
2848 s.y += (arguments[k+1]-arguments[k+3])*weight;
2849 denominator += weight;
2861 if ( bestfit && method != ArcDistortion ) {
2862 s.x -= image->page.x;
2863 s.y -= image->page.y;
2868 if ( validity <= 0.0 ) {
2870 SetPixelViaPixelInfo(distort_image,&invalid,q);
2874 status=ResamplePixelColor(resample_filter[
id],s.x,s.y,&pixel,
2876 if (status == MagickFalse)
2877 SetPixelViaPixelInfo(distort_image,&invalid,q);
2881 if ( validity < 1.0 ) {
2884 CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
2887 SetPixelViaPixelInfo(distort_image,&pixel,q);
2890 q+=(ptrdiff_t) GetPixelChannels(distort_image);
2892 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2893 if (sync == MagickFalse)
2895 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2900#if defined(MAGICKCORE_OPENMP_SUPPORT)
2904 proceed=SetImageProgress(image,DistortImageTag,progress,image->rows);
2905 if (proceed == MagickFalse)
2909 distort_view=DestroyCacheView(distort_view);
2910 resample_filter=DestroyResampleFilterTLS(resample_filter);
2912 if (status == MagickFalse)
2913 distort_image=DestroyImage(distort_image);
2919 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2920 distort_image->page.x = 0;
2921 distort_image->page.y = 0;
2923 coeff=(
double *) RelinquishMagickMemory(coeff);
2924 return(distort_image);
2961MagickExport
Image *RotateImage(
const Image *image,
const double degrees,
2980 assert(image != (
Image *) NULL);
2981 assert(image->signature == MagickCoreSignature);
2982 if (IsEventLogging() != MagickFalse)
2983 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2985 assert(exception->signature == MagickCoreSignature);
2986 angle=fmod(degrees,360.0);
2987 while (angle < -45.0)
2989 for (rotations=0; angle > 45.0; rotations++)
2992 shear.x=(-tan((
double) DegreesToRadians(angle)/2.0));
2993 shear.y=sin((
double) DegreesToRadians(angle));
2994 if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
2995 return(IntegralRotateImage(image,rotations,exception));
2996 distort_image=CloneImage(image,0,0,MagickTrue,exception);
2997 if (distort_image == (
Image *) NULL)
2998 return((
Image *) NULL);
2999 (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
3001 rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
3002 °rees,MagickTrue,exception);
3003 distort_image=DestroyImage(distort_image);
3004 return(rotate_image);
3046MagickExport
Image *SparseColorImage(
const Image *image,
3047 const SparseColorMethod method,
const size_t number_arguments,
3050#define SparseColorTag "Distort/SparseColor"
3064 assert(image != (
Image *) NULL);
3065 assert(image->signature == MagickCoreSignature);
3067 assert(exception->signature == MagickCoreSignature);
3068 if (IsEventLogging() != MagickFalse)
3069 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3073 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3075 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3077 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3079 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3080 (image->colorspace == CMYKColorspace))
3082 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3083 (image->alpha_trait != UndefinedPixelTrait))
3093 distort_method=(DistortMethod) method;
3094 if ( distort_method >= SentinelDistortion )
3095 distort_method = ShepardsDistortion;
3096 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
3097 arguments, number_colors, exception);
3098 if ( coeff == (
double *) NULL )
3099 return((
Image *) NULL);
3106 sparse_method = (SparseColorMethod) distort_method;
3107 if ( distort_method == ShepardsDistortion )
3108 sparse_method = method;
3109 if ( sparse_method == InverseColorInterpolate )
3114 if (IsStringTrue(GetImageArtifact(image,
"verbose")) != MagickFalse) {
3116 switch (sparse_method) {
3117 case BarycentricColorInterpolate:
3120 (void) FormatLocaleFile(stderr,
"Barycentric Sparse Color:\n");
3121 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3122 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
3123 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3124 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3125 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
3126 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3127 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3128 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
3129 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3130 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3131 (image->colorspace == CMYKColorspace))
3132 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
3133 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3134 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3135 (image->alpha_trait != UndefinedPixelTrait))
3136 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
3137 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3140 case BilinearColorInterpolate:
3143 (void) FormatLocaleFile(stderr,
"Bilinear Sparse Color\n");
3144 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3145 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3146 coeff[ x ], coeff[x+1],
3147 coeff[x+2], coeff[x+3]),x+=4;
3148 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3149 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3150 coeff[ x ], coeff[x+1],
3151 coeff[x+2], coeff[x+3]),x+=4;
3152 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3153 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3154 coeff[ x ], coeff[x+1],
3155 coeff[x+2], coeff[x+3]),x+=4;
3156 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3157 (image->colorspace == CMYKColorspace))
3158 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3159 coeff[ x ], coeff[x+1],
3160 coeff[x+2], coeff[x+3]),x+=4;
3161 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3162 (image->alpha_trait != UndefinedPixelTrait))
3163 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3164 coeff[ x ], coeff[x+1],
3165 coeff[x+2], coeff[x+3]),x+=4;
3180 sparse_image=CloneImage(image,0,0,MagickTrue,exception);
3181 if (sparse_image == (
Image *) NULL)
3182 return((
Image *) NULL);
3183 if (SetImageStorageClass(sparse_image,DirectClass,exception) == MagickFalse)
3185 sparse_image=DestroyImage(sparse_image);
3186 return((
Image *) NULL);
3188 if (IsGrayColorspace(sparse_image->colorspace) != MagickFalse)
3189 (void) SetImageColorspace(sparse_image,sRGBColorspace,exception);
3205 sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
3206#if defined(MAGICKCORE_OPENMP_SUPPORT)
3207 #pragma omp parallel for schedule(static) shared(progress,status) \
3208 magick_number_threads(image,sparse_image,sparse_image->rows,1)
3210 for (j=0; j < (ssize_t) sparse_image->rows; j++)
3224 q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,1,
3226 if (q == (Quantum *) NULL)
3231 GetPixelInfo(sparse_image,&pixel);
3232 for (i=0; i < (ssize_t) sparse_image->columns; i++)
3234 GetPixelInfoPixel(sparse_image,q,&pixel);
3235 switch (sparse_method)
3237 case BarycentricColorInterpolate:
3240 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3241 pixel.red = coeff[x]*i +coeff[x+1]*j
3243 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3244 pixel.green = coeff[x]*i +coeff[x+1]*j
3246 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3247 pixel.blue = coeff[x]*i +coeff[x+1]*j
3249 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3250 (sparse_image->colorspace == CMYKColorspace))
3251 pixel.black = coeff[x]*i +coeff[x+1]*j
3253 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3254 (sparse_image->alpha_trait != UndefinedPixelTrait))
3255 pixel.alpha = coeff[x]*i +coeff[x+1]*j
3259 case BilinearColorInterpolate:
3262 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3263 pixel.red = coeff[x]*i + coeff[x+1]*j +
3264 coeff[x+2]*i*j + coeff[x+3], x+=4;
3265 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3266 pixel.green = coeff[x]*i + coeff[x+1]*j +
3267 coeff[x+2]*i*j + coeff[x+3], x+=4;
3268 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3269 pixel.blue = coeff[x]*i + coeff[x+1]*j +
3270 coeff[x+2]*i*j + coeff[x+3], x+=4;
3271 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3272 (image->colorspace == CMYKColorspace))
3273 pixel.black = coeff[x]*i + coeff[x+1]*j +
3274 coeff[x+2]*i*j + coeff[x+3], x+=4;
3275 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3276 (sparse_image->alpha_trait != UndefinedPixelTrait))
3277 pixel.alpha = coeff[x]*i + coeff[x+1]*j +
3278 coeff[x+2]*i*j + coeff[x+3], x+=4;
3281 case InverseColorInterpolate:
3282 case ShepardsColorInterpolate:
3290 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3292 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3294 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3296 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3297 (image->colorspace == CMYKColorspace))
3299 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3300 (sparse_image->alpha_trait != UndefinedPixelTrait))
3303 for (k=0; k<number_arguments; k+=2+number_colors)
3306 ((double) i-arguments[ k ])*((double) i-arguments[ k ])
3307 + ((double) j-arguments[k+1])*((double) j-arguments[k+1]);
3308 ssize_t x = (ssize_t) k+2;
3310 weight = pow(weight,coeff[0]);
3311 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
3312 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3313 pixel.red += arguments[x++]*weight;
3314 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3315 pixel.green += arguments[x++]*weight;
3316 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3317 pixel.blue += arguments[x++]*weight;
3318 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3319 (image->colorspace == CMYKColorspace))
3320 pixel.black += arguments[x++]*weight;
3321 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3322 (sparse_image->alpha_trait != UndefinedPixelTrait))
3323 pixel.alpha += arguments[x++]*weight;
3324 denominator += weight;
3326 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3327 pixel.red/=denominator;
3328 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3329 pixel.green/=denominator;
3330 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3331 pixel.blue/=denominator;
3332 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3333 (image->colorspace == CMYKColorspace))
3334 pixel.black/=denominator;
3335 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3336 (sparse_image->alpha_trait != UndefinedPixelTrait))
3337 pixel.alpha/=denominator;
3340 case ManhattanColorInterpolate:
3343 minimum = MagickMaximumValue;
3351 for (k=0; k<number_arguments; k+=2+number_colors)
3353 double distance = fabs((
double)i-arguments[ k ])+
3354 fabs((
double)j-arguments[k+1]);
3355 if ( distance < minimum ) {
3356 ssize_t x=(ssize_t) k+2;
3357 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3358 pixel.red=arguments[x++];
3359 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3360 pixel.green=arguments[x++];
3361 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3362 pixel.blue=arguments[x++];
3363 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3364 (image->colorspace == CMYKColorspace))
3365 pixel.black=arguments[x++];
3366 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3367 (sparse_image->alpha_trait != UndefinedPixelTrait))
3368 pixel.alpha=arguments[x++];
3374 case VoronoiColorInterpolate:
3378 minimum = MagickMaximumValue;
3386 for (k=0; k<number_arguments; k+=2+number_colors) {
3388 ((double) i-arguments[ k ])*((double) i-arguments[ k ])
3389 + ((double) j-arguments[k+1])*((double) j-arguments[k+1]);
3390 if ( distance < minimum ) {
3391 ssize_t x = (ssize_t) k+2;
3392 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3393 pixel.red=arguments[x++];
3394 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3395 pixel.green=arguments[x++];
3396 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3397 pixel.blue=arguments[x++];
3398 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3399 (image->colorspace == CMYKColorspace))
3400 pixel.black=arguments[x++];
3401 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3402 (sparse_image->alpha_trait != UndefinedPixelTrait))
3403 pixel.alpha=arguments[x++];
3411 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3412 pixel.red=(MagickRealType) ClampPixel((
double) QuantumRange*
3414 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3415 pixel.green=(MagickRealType) ClampPixel((
double) QuantumRange*
3417 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3418 pixel.blue=(MagickRealType) ClampPixel((
double) QuantumRange*
3420 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3421 (image->colorspace == CMYKColorspace))
3422 pixel.black=(MagickRealType) ClampPixel((
double) QuantumRange*
3424 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3425 (image->alpha_trait != UndefinedPixelTrait))
3426 pixel.alpha=(MagickRealType) ClampPixel((
double) QuantumRange*
3428 SetPixelViaPixelInfo(sparse_image,&pixel,q);
3429 q+=(ptrdiff_t) GetPixelChannels(sparse_image);
3431 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
3432 if (sync == MagickFalse)
3434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3439#if defined(MAGICKCORE_OPENMP_SUPPORT)
3443 proceed=SetImageProgress(image,SparseColorTag,progress,image->rows);
3444 if (proceed == MagickFalse)
3448 sparse_view=DestroyCacheView(sparse_view);
3449 if (status == MagickFalse)
3450 sparse_image=DestroyImage(sparse_image);
3452 coeff = (
double *) RelinquishMagickMemory(coeff);
3453 return(sparse_image);