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=MagickSafeReciprocal(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=MagickSafeReciprocal(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(CastDoubleToSizeT(floor((order+1.0)*(order+2.0)/2.0)));
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,
284 const AffineMatrix *affine_matrix,ExceptionInfo *exception)
295 assert(image->signature == MagickCoreSignature);
296 assert(affine_matrix != (AffineMatrix *) NULL);
297 assert(exception != (ExceptionInfo *) NULL);
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,
374 size_t number_values,ExceptionInfo *exception)
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 if (number_arguments < 1)
428 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
429 "InvalidArgument",
"%s : '%s'",
"Polynomial",
430 "Needs at least 1 argument");
431 return((
double *) NULL);
433 i = poly_number_terms(arguments[0]);
434 number_coefficients = 2 + i*number_values;
437 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
438 "InvalidArgument",
"%s : '%s'",
"Polynomial",
439 "Invalid order, should be integer 1 to 5, or 1.5");
440 return((
double *) NULL);
442 if ((number_arguments < (1+i*cp_size)) ||
443 (((number_arguments-1) % cp_size) != 0))
445 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
446 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
447 "Polynomial", (
double) i);
448 return((
double *) NULL);
451 case BilinearReverseDistortion:
452 number_coefficients=4*number_values;
457 case BilinearForwardDistortion:
458 number_coefficients=10;
464 case QuadrilateralDistortion:
465 number_coefficients=19;
468 case ShepardsDistortion:
469 number_coefficients=1;
472 number_coefficients=5;
474 case ScaleRotateTranslateDistortion:
475 case AffineProjectionDistortion:
476 case Plane2CylinderDistortion:
477 case Cylinder2PlaneDistortion:
478 number_coefficients=6;
480 case PolarDistortion:
481 case DePolarDistortion:
482 number_coefficients=8;
484 case PerspectiveDistortion:
485 case PerspectiveProjectionDistortion:
486 number_coefficients=9;
488 case BarrelDistortion:
489 case BarrelInverseDistortion:
490 number_coefficients=10;
493 perror(
"unknown method given");
497 coeff=(
double *) AcquireQuantumMemory(number_coefficients,
sizeof(*coeff));
498 if (coeff == (
double *) NULL)
500 (void) ThrowMagickException(exception,GetMagickModule(),
501 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
502 "GenerateCoefficients");
503 return((
double *) NULL);
507 for (i=0; i < number_coefficients; i++)
512 case AffineDistortion:
522 if ( number_arguments%cp_size != 0 ||
523 number_arguments < cp_size ) {
524 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
525 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
527 coeff=(
double *) RelinquishMagickMemory(coeff);
528 return((
double *) NULL);
531 if ( number_arguments == cp_size ) {
533 if ( cp_values == 0 ) {
536 coeff[2] = arguments[0] - arguments[2];
538 coeff[5] = arguments[1] - arguments[3];
542 for (i=0; i<number_values; i++)
543 coeff[i*3+2] = arguments[cp_values+i];
559 matrix=AcquireMagickMatrix(3UL,3UL);
560 vectors=(
double **) AcquireQuantumMemory(number_values,
562 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
564 matrix = RelinquishMagickMatrix(matrix, 3UL);
565 vectors = (
double **) RelinquishMagickMemory(vectors);
566 coeff = (
double *) RelinquishMagickMemory(coeff);
567 (void) ThrowMagickException(exception,GetMagickModule(),
568 ResourceLimitError,
"MemoryAllocationFailed",
569 "%s",
"DistortCoefficients");
570 return((
double *) NULL);
573 for (i=0; i < number_values; i++)
574 vectors[i] = &(coeff[i*3]);
576 for (i=0; i < number_arguments; i+=cp_size) {
577 terms[0] = arguments[i+cp_x];
578 terms[1] = arguments[i+cp_y];
580 LeastSquaresAddTerms(matrix,vectors,terms,
581 &(arguments[i+cp_values]),3UL,number_values);
583 if ( number_arguments == 2*cp_size ) {
588 terms[0] = arguments[cp_x]
589 - ( arguments[cp_size+cp_y] - arguments[cp_y] );
590 terms[1] = arguments[cp_y] +
591 + ( arguments[cp_size+cp_x] - arguments[cp_x] );
593 if ( cp_values == 0 ) {
597 uv2[0] = arguments[0] - arguments[5] + arguments[1];
598 uv2[1] = arguments[1] + arguments[4] - arguments[0];
599 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
603 LeastSquaresAddTerms(matrix,vectors,terms,
604 &(arguments[cp_values]),3UL,number_values);
608 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
609 matrix = RelinquishMagickMatrix(matrix, 3UL);
610 vectors = (
double **) RelinquishMagickMemory(vectors);
611 if ( status == MagickFalse ) {
612 coeff = (
double *) RelinquishMagickMemory(coeff);
613 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
614 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
615 CommandOptionToMnemonic(MagickDistortOptions, *method) );
616 return((
double *) NULL);
621 case RigidAffineDistortion:
637 if (((number_arguments % cp_size) != 0) || (number_arguments < cp_size))
639 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
640 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
641 CommandOptionToMnemonic(MagickDistortOptions,*method),2.0);
642 coeff=(
double *) RelinquishMagickMemory(coeff);
643 return((
double *) NULL);
648 matrix=AcquireMagickMatrix(4UL,4UL);
649 if (matrix == (
double **) NULL)
651 coeff=(
double *) RelinquishMagickMemory(coeff);
652 (void) ThrowMagickException(exception,GetMagickModule(),
653 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
654 CommandOptionToMnemonic(MagickDistortOptions,*method));
655 return((
double *) NULL);
660 vectors[0]=(&(coeff[0]));
661 for (i=0; i < number_arguments; i+=4)
663 terms[0]=arguments[i+0];
664 terms[1]=(-arguments[i+1]);
667 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+2]),4UL,1UL);
668 terms[0]=arguments[i+1];
669 terms[1]=arguments[i+0];
672 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+3]),4UL,1UL);
677 status=GaussJordanElimination(matrix,vectors,4UL,1UL);
678 matrix=RelinquishMagickMatrix(matrix,4UL);
679 if (status == MagickFalse)
681 coeff=(
double *) RelinquishMagickMemory(coeff);
682 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
683 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
684 CommandOptionToMnemonic(MagickDistortOptions,*method));
685 return((
double *) NULL);
692 inverse[2]=(-coeff[1]);
696 AffineArgsToCoefficients(inverse);
697 InvertAffineCoefficients(inverse,coeff);
698 *method=AffineDistortion;
701 case AffineProjectionDistortion:
717 if (number_arguments != 6) {
718 coeff = (
double *) RelinquishMagickMemory(coeff);
719 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
720 "InvalidArgument",
"%s : 'Needs 6 coeff values'",
721 CommandOptionToMnemonic(MagickDistortOptions, *method) );
722 return((
double *) NULL);
725 for(i=0; i<6UL; i++ )
726 inverse[i] = arguments[i];
727 AffineArgsToCoefficients(inverse);
728 InvertAffineCoefficients(inverse, coeff);
729 *method = AffineDistortion;
733 case ScaleRotateTranslateDistortion:
762 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
763 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
765 switch ( number_arguments ) {
767 coeff = (
double *) RelinquishMagickMemory(coeff);
768 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
769 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
770 CommandOptionToMnemonic(MagickDistortOptions, *method) );
771 return((
double *) NULL);
776 sx = sy = arguments[0];
780 x = nx = arguments[0];
781 y = ny = arguments[1];
782 switch ( number_arguments ) {
787 sx = sy = arguments[2];
796 sx = sy = arguments[2];
809 coeff = (
double *) RelinquishMagickMemory(coeff);
810 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
811 "InvalidArgument",
"%s : 'Too Many Arguments (7 or less)'",
812 CommandOptionToMnemonic(MagickDistortOptions, *method) );
813 return((
double *) NULL);
818 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
819 coeff = (
double *) RelinquishMagickMemory(coeff);
820 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
821 "InvalidArgument",
"%s : 'Zero Scale Given'",
822 CommandOptionToMnemonic(MagickDistortOptions, *method) );
823 return((
double *) NULL);
826 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
828 *method = AffineDistortion;
831 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
834 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
837 case PerspectiveDistortion:
880 if ( number_arguments%cp_size != 0 ||
881 number_arguments < cp_size*4 ) {
882 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
883 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
884 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
885 coeff=(
double *) RelinquishMagickMemory(coeff);
886 return((
double *) NULL);
889 vectors[0] = &(coeff[0]);
891 matrix = AcquireMagickMatrix(8UL,8UL);
892 if (matrix == (
double **) NULL) {
893 coeff=(
double *) RelinquishMagickMemory(coeff);
894 (void) ThrowMagickException(exception,GetMagickModule(),
895 ResourceLimitError,
"MemoryAllocationFailed",
896 "%s",
"DistortCoefficients");
897 return((
double *) NULL);
900 for (i=0; i < number_arguments; i+=4) {
901 terms[0]=arguments[i+cp_x];
902 terms[1]=arguments[i+cp_y];
907 terms[6]=-terms[0]*arguments[i+cp_u];
908 terms[7]=-terms[1]*arguments[i+cp_u];
909 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
915 terms[3]=arguments[i+cp_x];
916 terms[4]=arguments[i+cp_y];
918 terms[6]=-terms[3]*arguments[i+cp_v];
919 terms[7]=-terms[4]*arguments[i+cp_v];
920 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
924 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
925 matrix = RelinquishMagickMatrix(matrix, 8UL);
926 if ( status == MagickFalse ) {
927 coeff = (
double *) RelinquishMagickMemory(coeff);
928 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
929 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
930 CommandOptionToMnemonic(MagickDistortOptions, *method) );
931 return((
double *) NULL);
939 coeff[8] = coeff[6]*arguments[cp_x]
940 + coeff[7]*arguments[cp_y] + 1.0;
941 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
945 case PerspectiveProjectionDistortion:
950 if (number_arguments != 8) {
951 coeff = (
double *) RelinquishMagickMemory(coeff);
952 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
953 "InvalidArgument",
"%s : 'Needs 8 coefficient values'",
954 CommandOptionToMnemonic(MagickDistortOptions, *method));
955 return((
double *) NULL);
958 InvertPerspectiveCoefficients(arguments, coeff);
966 coeff[8] = coeff[6]*arguments[2]
967 + coeff[7]*arguments[5] + 1.0;
968 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
969 *method = PerspectiveDistortion;
973 case BilinearForwardDistortion:
974 case BilinearReverseDistortion:
998 if ( number_arguments%cp_size != 0 ||
999 number_arguments < cp_size*4 ) {
1000 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1001 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
1002 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
1003 coeff=(
double *) RelinquishMagickMemory(coeff);
1004 return((
double *) NULL);
1007 matrix=AcquireMagickMatrix(4UL,4UL);
1008 vectors=(
double **) AcquireQuantumMemory(number_values,
sizeof(*vectors));
1009 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
1011 matrix = RelinquishMagickMatrix(matrix, 4UL);
1012 vectors = (
double **) RelinquishMagickMemory(vectors);
1013 coeff = (
double *) RelinquishMagickMemory(coeff);
1014 (void) ThrowMagickException(exception,GetMagickModule(),
1015 ResourceLimitError,
"MemoryAllocationFailed",
1016 "%s",
"DistortCoefficients");
1017 return((
double *) NULL);
1020 for (i=0; i < number_values; i++)
1021 vectors[i] = &(coeff[i*4]);
1023 for (i=0; i < number_arguments; i+=cp_size) {
1024 terms[0] = arguments[i+cp_x];
1025 terms[1] = arguments[i+cp_y];
1026 terms[2] = terms[0]*terms[1];
1028 LeastSquaresAddTerms(matrix,vectors,terms,
1029 &(arguments[i+cp_values]),4UL,number_values);
1032 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
1033 matrix = RelinquishMagickMatrix(matrix, 4UL);
1034 vectors = (
double **) RelinquishMagickMemory(vectors);
1035 if ( status == MagickFalse ) {
1036 coeff = (
double *) RelinquishMagickMemory(coeff);
1037 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1038 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
1039 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1040 return((
double *) NULL);
1042 if ( *method == BilinearForwardDistortion ) {
1082 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
1083 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
1088 case QuadrilateralDistortion:
1104 case PolynomialDistortion:
1142 coeff[0] = arguments[0];
1143 coeff[1] = (double) poly_number_terms(arguments[0]);
1144 nterms = CastDoubleToSizeT(coeff[1]);
1147 matrix=AcquireMagickMatrix(nterms,nterms);
1148 vectors=(
double **) AcquireQuantumMemory(number_values,
1150 terms=(
double *) AcquireQuantumMemory(nterms,
sizeof(*terms));
1151 if ((matrix == (
double **) NULL) || (vectors == (
double **) NULL) ||
1152 (terms == (
double *) NULL))
1154 matrix = RelinquishMagickMatrix(matrix, nterms);
1155 vectors = (
double **) RelinquishMagickMemory(vectors);
1156 terms = (
double *) RelinquishMagickMemory(terms);
1157 coeff = (
double *) RelinquishMagickMemory(coeff);
1158 (void) ThrowMagickException(exception,GetMagickModule(),
1159 ResourceLimitError,
"MemoryAllocationFailed",
1160 "%s",
"DistortCoefficients");
1161 return((
double *) NULL);
1164 for (i=0; i < number_values; i++)
1165 vectors[i] = &(coeff[2+i*nterms]);
1167 for (i=1; i < number_arguments; i+=cp_size) {
1168 for (j=0; j < (ssize_t) nterms; j++)
1169 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1170 LeastSquaresAddTerms(matrix,vectors,terms,
1171 &(arguments[i+cp_values]),nterms,number_values);
1173 terms = (
double *) RelinquishMagickMemory(terms);
1175 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1176 matrix = RelinquishMagickMatrix(matrix, nterms);
1177 vectors = (
double **) RelinquishMagickMemory(vectors);
1178 if ( status == MagickFalse ) {
1179 coeff = (
double *) RelinquishMagickMemory(coeff);
1180 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1181 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
1182 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1183 return((
double *) NULL);
1223 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1224 coeff = (
double *) RelinquishMagickMemory(coeff);
1225 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1226 "InvalidArgument",
"%s : 'Arc Angle Too Small'",
1227 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1228 return((
double *) NULL);
1230 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1231 coeff = (
double *) RelinquishMagickMemory(coeff);
1232 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1233 "InvalidArgument",
"%s : 'Outer Radius Too Small'",
1234 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1235 return((
double *) NULL);
1237 coeff[0] = -MagickPI2;
1238 if ( number_arguments >= 1 )
1239 coeff[1] = DegreesToRadians(arguments[0]);
1241 coeff[1] = MagickPI2;
1242 if ( number_arguments >= 2 )
1243 coeff[0] += DegreesToRadians(arguments[1]);
1244 coeff[0] /= Magick2PI;
1245 coeff[0] -= MagickRound(coeff[0]);
1246 coeff[0] *= Magick2PI;
1247 coeff[3] = (double)image->rows-1;
1248 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1249 if ( number_arguments >= 3 ) {
1250 if ( number_arguments >= 4 )
1251 coeff[3] = arguments[2] - arguments[3];
1253 coeff[3] *= arguments[2]/coeff[2];
1254 coeff[2] = arguments[2];
1256 coeff[4] = ((double)image->columns-1.0)/2.0;
1260 case PolarDistortion:
1261 case DePolarDistortion:
1273 if ( number_arguments == 3
1274 || ( number_arguments > 6 && *method == PolarDistortion )
1275 || number_arguments > 8 ) {
1276 (void) ThrowMagickException(exception,GetMagickModule(),
1277 OptionError,
"InvalidArgument",
"%s : number of arguments",
1278 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1279 coeff=(
double *) RelinquishMagickMemory(coeff);
1280 return((
double *) NULL);
1283 if ( number_arguments >= 1 )
1284 coeff[0] = arguments[0];
1288 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1290 if ( number_arguments >= 4 ) {
1291 coeff[2] = arguments[2];
1292 coeff[3] = arguments[3];
1295 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1296 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1299 coeff[4] = -MagickPI;
1300 if ( number_arguments >= 5 )
1301 coeff[4] = DegreesToRadians(arguments[4]);
1302 coeff[5] = coeff[4];
1303 if ( number_arguments >= 6 )
1304 coeff[5] = DegreesToRadians(arguments[5]);
1305 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1306 coeff[5] += Magick2PI;
1308 if ( coeff[0] < MagickEpsilon ) {
1310 if ( fabs(coeff[0]) < MagickEpsilon ) {
1311 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1312 fabs(coeff[3]-image->page.y));
1313 coeff[0]=MagickMin(coeff[0],
1314 fabs(coeff[2]-image->page.x-image->columns));
1315 coeff[0]=MagickMin(coeff[0],
1316 fabs(coeff[3]-image->page.y-image->rows));
1319 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1321 rx = coeff[2]-image->page.x;
1322 ry = coeff[3]-image->page.y;
1323 coeff[0] = rx*rx+ry*ry;
1324 ry = coeff[3]-image->page.y-image->rows;
1325 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1326 rx = coeff[2]-image->page.x-image->columns;
1327 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1328 ry = coeff[3]-image->page.y;
1329 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1330 coeff[0] = sqrt(coeff[0]);
1334 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1335 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1336 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1337 "InvalidArgument",
"%s : Invalid Radius",
1338 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1339 coeff=(
double *) RelinquishMagickMemory(coeff);
1340 return((
double *) NULL);
1343 if ( *method == PolarDistortion ) {
1344 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1345 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1348 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1349 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1353 case Cylinder2PlaneDistortion:
1354 case Plane2CylinderDistortion:
1378 if (number_arguments < 1) {
1379 coeff=(
double *) RelinquishMagickMemory(coeff);
1380 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1381 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
1382 CommandOptionToMnemonic(MagickDistortOptions,*method));
1383 return((
double *) NULL);
1385 if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
1386 coeff=(
double *) RelinquishMagickMemory(coeff);
1387 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1388 "InvalidArgument",
"%s : Invalid FOV Angle",
1389 CommandOptionToMnemonic(MagickDistortOptions,*method));
1390 return((
double *) NULL);
1392 coeff[0] = DegreesToRadians(arguments[0]);
1393 if ( *method == Cylinder2PlaneDistortion )
1397 coeff[1] = (double) image->columns/coeff[0];
1400 coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );
1402 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1403 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1404 coeff[4] = coeff[2];
1405 coeff[5] = coeff[3];
1408 case BarrelDistortion:
1409 case BarrelInverseDistortion:
1430 rscale = 2.0/MagickMin((
double) image->columns,(
double) image->rows);
1433 if ( (number_arguments < 3) || (number_arguments == 7) ||
1434 (number_arguments == 9) || (number_arguments > 10) )
1436 coeff=(
double *) RelinquishMagickMemory(coeff);
1437 (void) ThrowMagickException(exception,GetMagickModule(),
1438 OptionError,
"InvalidArgument",
"%s : number of arguments",
1439 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1440 return((
double *) NULL);
1443 coeff[0] = arguments[0];
1444 coeff[1] = arguments[1];
1445 coeff[2] = arguments[2];
1446 if ((number_arguments == 3) || (number_arguments == 5) )
1447 coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1449 coeff[3] = arguments[3];
1451 coeff[0] *= pow(rscale,3.0);
1452 coeff[1] *= rscale*rscale;
1455 if ( number_arguments >= 8 ) {
1456 coeff[4] = arguments[4] * pow(rscale,3.0);
1457 coeff[5] = arguments[5] * rscale*rscale;
1458 coeff[6] = arguments[6] * rscale;
1459 coeff[7] = arguments[7];
1462 coeff[4] = coeff[0];
1463 coeff[5] = coeff[1];
1464 coeff[6] = coeff[2];
1465 coeff[7] = coeff[3];
1468 if ( number_arguments == 5 ) {
1469 coeff[8] = arguments[3];
1470 coeff[9] = arguments[4];
1472 else if ( number_arguments == 6 ) {
1473 coeff[8] = arguments[4];
1474 coeff[9] = arguments[5];
1476 else if ( number_arguments == 10 ) {
1477 coeff[8] = arguments[8];
1478 coeff[9] = arguments[9];
1482 coeff[8] = (double)image->columns/2.0 + image->page.x;
1483 coeff[9] = (double)image->rows/2.0 + image->page.y;
1487 case ShepardsDistortion:
1494 if ( number_arguments%cp_size != 0 ||
1495 number_arguments < cp_size ) {
1496 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1497 "InvalidArgument",
"%s : 'requires CP's (4 numbers each)'",
1498 CommandOptionToMnemonic(MagickDistortOptions, *method));
1499 coeff=(
double *) RelinquishMagickMemory(coeff);
1500 return((
double *) NULL);
1503 {
const char *artifact=GetImageArtifact(image,
"shepards:power");
1504 if ( artifact != (
const char *) NULL ) {
1505 coeff[0]=StringToDouble(artifact,(
char **) NULL) / 2.0;
1506 if ( coeff[0] < MagickEpsilon ) {
1507 (void) ThrowMagickException(exception,GetMagickModule(),
1508 OptionError,
"InvalidArgument",
"%s",
"-define shepards:power" );
1509 coeff=(
double *) RelinquishMagickMemory(coeff);
1510 return((
double *) NULL);
1522 perror(
"no method handler");
1523 return((
double *) NULL);
1561MagickExport Image *DistortResizeImage(
const Image *image,
const size_t columns,
1562 const size_t rows,ExceptionInfo *exception)
1564#define DistortResizeImageTag "Distort/Image"
1582 assert(image != (
const Image *) NULL);
1583 assert(image->signature == MagickCoreSignature);
1584 assert(exception != (ExceptionInfo *) NULL);
1585 assert(exception->signature == MagickCoreSignature);
1586 if (IsEventLogging() != MagickFalse)
1587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1588 if ((columns == 0) || (rows == 0))
1589 return((Image *) NULL);
1592 (void) memset(distort_args,0,
sizeof(distort_args));
1593 distort_args[4]=(double) image->columns;
1594 distort_args[6]=(double) columns;
1595 distort_args[9]=(double) image->rows;
1596 distort_args[11]=(double) rows;
1598 vp_save=GetImageVirtualPixelMethod(image);
1600 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1601 if (tmp_image == (Image *) NULL)
1602 return((Image *) NULL);
1603 (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
1606 if ((image->alpha_trait & BlendPixelTrait) == 0)
1611 (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel,exception);
1612 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1613 MagickTrue,exception),
1614 tmp_image=DestroyImage(tmp_image);
1615 if (resize_image == (Image *) NULL)
1616 return((Image *) NULL);
1617 (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1631 (void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
1632 (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
1633 resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1634 MagickTrue,exception),
1635 tmp_image=DestroyImage(tmp_image);
1636 if (resize_alpha == (Image *) NULL)
1637 return((Image *) NULL);
1640 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1641 if (tmp_image == (Image *) NULL)
1642 return((Image *) NULL);
1643 (void) SetImageVirtualPixelMethod(tmp_image,
1644 TransparentVirtualPixelMethod,exception);
1645 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1646 MagickTrue,exception),
1647 tmp_image=DestroyImage(tmp_image);
1648 if (resize_image == (Image *) NULL)
1650 resize_alpha=DestroyImage(resize_alpha);
1651 return((Image *) NULL);
1654 (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1655 (void) SetImageAlphaChannel(resize_alpha,OffAlphaChannel,exception);
1656 (void) CompositeImage(resize_image,resize_alpha,CopyAlphaCompositeOp,
1657 MagickTrue,0,0,exception);
1658 resize_alpha=DestroyImage(resize_alpha);
1659 resize_image->alpha_trait=image->alpha_trait;
1660 resize_image->compose=image->compose;
1662 (void) SetImageVirtualPixelMethod(resize_image,vp_save,exception);
1667 crop_area.width=columns;
1668 crop_area.height=rows;
1672 tmp_image=resize_image;
1673 resize_image=CropImage(tmp_image,&crop_area,exception);
1674 tmp_image=DestroyImage(tmp_image);
1675 if (resize_image != (Image *) NULL)
1677 resize_image->page.width=0;
1678 resize_image->page.height=0;
1680 return(resize_image);
1771MagickExport Image *DistortImage(
const Image *image, DistortMethod method,
1772 const size_t number_arguments,
const double *arguments,
1773 MagickBooleanType bestfit,ExceptionInfo *exception)
1775#define DistortImageTag "Distort/Image"
1793 assert(image != (Image *) NULL);
1794 assert(image->signature == MagickCoreSignature);
1795 assert(exception != (ExceptionInfo *) NULL);
1796 assert(exception->signature == MagickCoreSignature);
1797 if (IsEventLogging() != MagickFalse)
1798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1802 if ( method == ResizeDistortion )
1804 if ( number_arguments != 2 )
1806 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1807 "InvalidArgument",
"%s : '%s'",
"Resize",
1808 "Invalid number of args: 2 only");
1809 return((Image *) NULL);
1811 distort_image=DistortResizeImage(image,CastDoubleToSizeT(arguments[0]),
1812 CastDoubleToSizeT(arguments[1]),exception);
1813 return(distort_image);
1823 coeff = GenerateCoefficients(image, &method, number_arguments,
1824 arguments, 0, exception);
1825 if ( coeff == (
double *) NULL )
1826 return((Image *) NULL);
1834 geometry.width=image->columns;
1835 geometry.height=image->rows;
1839 if ( method == ArcDistortion ) {
1840 bestfit = MagickTrue;
1849 fix_bounds = MagickTrue;
1851 s.x=s.y=min.x=max.x=min.y=max.y=0.0;
1854#define InitalBounds(p) \
1857 min.x = max.x = p.x; \
1858 min.y = max.y = p.y; \
1860#define ExpandBounds(p) \
1863 min.x = MagickMin(min.x,p.x); \
1864 max.x = MagickMax(max.x,p.x); \
1865 min.y = MagickMin(min.y,p.y); \
1866 max.y = MagickMax(max.y,p.y); \
1871 case AffineDistortion:
1872 case RigidAffineDistortion:
1873 {
double inverse[6];
1874 InvertAffineCoefficients(coeff, inverse);
1875 s.x = (double) image->page.x;
1876 s.y = (double) image->page.y;
1877 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1878 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1880 s.x = (double) image->page.x+image->columns;
1881 s.y = (double) image->page.y;
1882 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1883 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1885 s.x = (double) image->page.x;
1886 s.y = (double) image->page.y+image->rows;
1887 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1888 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1890 s.x = (double) image->page.x+image->columns;
1891 s.y = (double) image->page.y+image->rows;
1892 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1893 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1897 case PerspectiveDistortion:
1898 {
double inverse[8], scale;
1899 InvertPerspectiveCoefficients(coeff, inverse);
1900 s.x = (double) image->page.x;
1901 s.y = (double) image->page.y;
1902 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1903 scale=MagickSafeReciprocal(scale);
1904 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1905 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1907 s.x = (double) image->page.x+image->columns;
1908 s.y = (double) image->page.y;
1909 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1910 scale=MagickSafeReciprocal(scale);
1911 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1912 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1914 s.x = (double) image->page.x;
1915 s.y = (double) image->page.y+image->rows;
1916 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1917 scale=MagickSafeReciprocal(scale);
1918 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1919 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1921 s.x = (double) image->page.x+image->columns;
1922 s.y = (double) image->page.y+image->rows;
1923 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1924 scale=MagickSafeReciprocal(scale);
1925 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1926 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1933 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1937 d.x = (coeff[2]-coeff[3])*ca;
1938 d.y = (coeff[2]-coeff[3])*sa;
1940 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1944 d.x = (coeff[2]-coeff[3])*ca;
1945 d.y = (coeff[2]-coeff[3])*sa;
1948 for( a=(
double) (ceil((
double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
1949 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1950 ca = cos(a); sa = sin(a);
1960 coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
1961 coeff[3] = (double)image->rows/coeff[3];
1964 case PolarDistortion:
1966 if (number_arguments < 2)
1967 coeff[2] = coeff[3] = 0.0;
1968 min.x = coeff[2]-coeff[0];
1969 max.x = coeff[2]+coeff[0];
1970 min.y = coeff[3]-coeff[0];
1971 max.y = coeff[3]+coeff[0];
1973 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1976 case DePolarDistortion:
1980 fix_bounds = MagickFalse;
1981 geometry.x = geometry.y = 0;
1982 geometry.height = CastDoubleToSizeT(ceil(coeff[0]-coeff[1]));
1983 geometry.width = CastDoubleToSizeT(ceil((coeff[0]-coeff[1])*
1984 (coeff[5]-coeff[4])*0.5));
1986 coeff[6]=(coeff[5]-coeff[4]) * MagickSafeReciprocal(
1987 (
double) geometry.width);
1988 coeff[7]=(coeff[0]-coeff[1]) * MagickSafeReciprocal(
1989 (
double) geometry.height);
1992 case Cylinder2PlaneDistortion:
1997 geometry.x = geometry.y = 0;
1998 geometry.width = CastDoubleToSizeT(ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) ));
1999 geometry.height = CastDoubleToSizeT(ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) ));
2001 coeff[4] = (double) geometry.width/2.0;
2002 coeff[5] = (double) geometry.height/2.0;
2003 fix_bounds = MagickFalse;
2006 case Plane2CylinderDistortion:
2010 geometry.x = geometry.y = 0;
2011 geometry.width = CastDoubleToSizeT(ceil(coeff[0]*coeff[1]));
2012 geometry.height = CastDoubleToSizeT(2.0*coeff[3]);
2014 coeff[4] = (double) geometry.width/2.0;
2015 coeff[5] = (double) geometry.height/2.0;
2016 fix_bounds = MagickFalse;
2019 case ShepardsDistortion:
2020 case BilinearForwardDistortion:
2021 case BilinearReverseDistortion:
2023 case QuadrilateralDistortion:
2025 case PolynomialDistortion:
2026 case BarrelDistortion:
2027 case BarrelInverseDistortion:
2030 bestfit = MagickFalse;
2031 fix_bounds = MagickFalse;
2040 geometry.x = CastDoubleToSsizeT(floor(min.x-0.5));
2041 geometry.y = CastDoubleToSsizeT(floor(min.y-0.5));
2042 geometry.width=CastDoubleToSizeT(ceil(max.x-geometry.x+0.5));
2043 geometry.height=CastDoubleToSizeT(ceil(max.y-geometry.y+0.5));
2052 {
const char *artifact=GetImageArtifact(image,
"distort:viewport");
2053 viewport_given = MagickFalse;
2054 if ( artifact != (
const char *) NULL ) {
2055 MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
2057 (void) ThrowMagickException(exception,GetMagickModule(),
2058 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
2059 "distort:viewport",artifact);
2061 viewport_given = MagickTrue;
2066 if (IsStringTrue(GetImageArtifact(image,
"verbose")) != MagickFalse) {
2069 char image_gen[MagickPathExtent];
2073 if ( bestfit || viewport_given ) {
2074 (void) FormatLocaleString(image_gen,MagickPathExtent,
2075 " -size %.20gx%.20g -page %+.20g%+.20g xc: +insert \\\n",
2076 (
double) geometry.width,(
double) geometry.height,(
double) geometry.x,
2077 (
double) geometry.y);
2078 lookup=
"v.p{xx-v.page.x-0.5,yy-v.page.y-0.5}";
2081 image_gen[0] =
'\0';
2082 lookup =
"p{xx-page.x-0.5,yy-page.y-0.5}";
2087 case AffineDistortion:
2088 case RigidAffineDistortion:
2093 inverse=(
double *) AcquireQuantumMemory(6,
sizeof(*inverse));
2094 if (inverse == (
double *) NULL)
2096 coeff=(
double *) RelinquishMagickMemory(coeff);
2097 (void) ThrowMagickException(exception,GetMagickModule(),
2098 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
"DistortImages");
2099 return((Image *) NULL);
2101 InvertAffineCoefficients(coeff, inverse);
2102 CoefficientsToAffineArgs(inverse);
2103 (void) FormatLocaleFile(stderr,
"Affine projection:\n");
2104 (void) FormatLocaleFile(stderr,
2105 " -distort AffineProjection \\\n '");
2106 for (i=0; i < 5; i++)
2107 (
void) FormatLocaleFile(stderr,
"%.*g,",GetMagickPrecision(),
2109 (void) FormatLocaleFile(stderr,
"%.*g'\n",GetMagickPrecision(),
2111 (void) FormatLocaleFile(stderr,
2112 "Equivalent scale, rotation(deg), translation:\n");
2113 (void) FormatLocaleFile(stderr,
" %.*g,%.*g,%.*g,%.*g\n",
2114 GetMagickPrecision(),sqrt(inverse[0]*inverse[0]+
2115 inverse[1]*inverse[1]),GetMagickPrecision(),
2116 RadiansToDegrees(atan2(inverse[1],inverse[0])),
2117 GetMagickPrecision(),inverse[4],GetMagickPrecision(),inverse[5]);
2118 inverse=(
double *) RelinquishMagickMemory(inverse);
2119 (void) FormatLocaleFile(stderr,
"Affine distort, FX equivalent:\n");
2120 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2121 (void) FormatLocaleFile(stderr,
2122 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2123 (void) FormatLocaleFile(stderr,
" xx=%+.*g*ii %+.*g*jj %+.*g;\n",
2124 GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2125 GetMagickPrecision(),coeff[2]);
2126 (void) FormatLocaleFile(stderr,
" yy=%+.*g*ii %+.*g*jj %+.*g;\n",
2127 GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2128 GetMagickPrecision(),coeff[5]);
2129 (void) FormatLocaleFile(stderr,
" %s' \\\n",lookup);
2132 case PerspectiveDistortion:
2137 inverse=(
double *) AcquireQuantumMemory(8,
sizeof(*inverse));
2138 if (inverse == (
double *) NULL)
2140 coeff=(
double *) RelinquishMagickMemory(coeff);
2141 (void) ThrowMagickException(exception,GetMagickModule(),
2142 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
2143 "DistortCoefficients");
2144 return((Image *) NULL);
2146 InvertPerspectiveCoefficients(coeff, inverse);
2147 (void) FormatLocaleFile(stderr,
"Perspective Projection:\n");
2148 (void) FormatLocaleFile(stderr,
2149 " -distort PerspectiveProjection \\\n '");
2150 for (i=0; i < 4; i++)
2151 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2153 (void) FormatLocaleFile(stderr,
"\n ");
2155 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2157 (void) FormatLocaleFile(stderr,
"%.*g'\n",GetMagickPrecision(),
2159 inverse=(
double *) RelinquishMagickMemory(inverse);
2160 (void) FormatLocaleFile(stderr,
"Perspective Distort, FX Equivalent:\n");
2161 (void) FormatLocaleFile(stderr,
"%.1024s",image_gen);
2162 (void) FormatLocaleFile(stderr,
2163 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2164 (void) FormatLocaleFile(stderr,
" rr=%+.*g*ii %+.*g*jj + 1;\n",
2165 GetMagickPrecision(),coeff[6],GetMagickPrecision(),coeff[7]);
2166 (void) FormatLocaleFile(stderr,
2167 " xx=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2168 GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2169 GetMagickPrecision(),coeff[2]);
2170 (void) FormatLocaleFile(stderr,
2171 " yy=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2172 GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2173 GetMagickPrecision(),coeff[5]);
2174 (void) FormatLocaleFile(stderr,
" rr%s0 ? %s : blue' \\\n",
2175 coeff[8] < 0.0 ?
"<" :
">", lookup);
2178 case BilinearForwardDistortion:
2180 (void) FormatLocaleFile(stderr,
"BilinearForward Mapping Equations:\n");
2181 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2182 (void) FormatLocaleFile(stderr,
" i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2183 coeff[0],coeff[1],coeff[2],coeff[3]);
2184 (void) FormatLocaleFile(stderr,
" j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2185 coeff[4],coeff[5],coeff[6],coeff[7]);
2188 (void) FormatLocaleFile(stderr,
" c8 = %+lf c9 = 2*a = %+lf;\n",
2189 coeff[8], coeff[9]);
2191 (void) FormatLocaleFile(stderr,
2192 "BilinearForward Distort, FX Equivalent:\n");
2193 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2194 (void) FormatLocaleFile(stderr,
2195 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",0.5-coeff[3],0.5-
2197 (void) FormatLocaleFile(stderr,
" bb=%lf*ii %+lf*jj %+lf;\n",
2198 coeff[6], -coeff[2], coeff[8]);
2202 (void) FormatLocaleFile(stderr,
2203 " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",-2*coeff[9],coeff[4],
2205 (void) FormatLocaleFile(stderr,
2206 " yy=( -bb + sqrt(rt) ) / %lf;\n",coeff[9]);
2209 (
void) FormatLocaleFile(stderr,
" yy=(%lf*ii%+lf*jj)/bb;\n",
2210 -coeff[4],coeff[0]);
2211 (void) FormatLocaleFile(stderr,
2212 " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",-coeff[1],coeff[0],
2214 if ( coeff[9] != 0 )
2215 (void) FormatLocaleFile(stderr,
" (rt < 0 ) ? red : %s'\n",
2218 (
void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2221 case BilinearReverseDistortion:
2224 (void) FormatLocaleFile(stderr,
"Polynomial Projection Distort:\n");
2225 (void) FormatLocaleFile(stderr,
" -distort PolynomialProjection \\\n");
2226 (void) FormatLocaleFile(stderr,
" '1.5, %lf, %lf, %lf, %lf,\n",
2227 coeff[3], coeff[0], coeff[1], coeff[2]);
2228 (void) FormatLocaleFile(stderr,
" %lf, %lf, %lf, %lf'\n",
2229 coeff[7], coeff[4], coeff[5], coeff[6]);
2231 (void) FormatLocaleFile(stderr,
2232 "BilinearReverse Distort, FX Equivalent:\n");
2233 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2234 (void) FormatLocaleFile(stderr,
2235 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2236 (void) FormatLocaleFile(stderr,
2237 " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[0],coeff[1],
2238 coeff[2], coeff[3]);
2239 (void) FormatLocaleFile(stderr,
2240 " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[4],coeff[5],
2241 coeff[6], coeff[7]);
2242 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2245 case PolynomialDistortion:
2247 size_t nterms = CastDoubleToSizeT(coeff[1]);
2248 (void) FormatLocaleFile(stderr,
2249 "Polynomial (order %lg, terms %lu), FX Equivalent\n",coeff[0],
2250 (
unsigned long) nterms);
2251 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2252 (void) FormatLocaleFile(stderr,
2253 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2254 (void) FormatLocaleFile(stderr,
" xx =");
2255 for (i=0; i < (ssize_t) nterms; i++)
2257 if ((i != 0) && (i%4 == 0))
2258 (void) FormatLocaleFile(stderr,
"\n ");
2259 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i],
2262 (void) FormatLocaleFile(stderr,
";\n yy =");
2263 for (i=0; i < (ssize_t) nterms; i++)
2265 if ((i != 0) && (i%4 == 0))
2266 (void) FormatLocaleFile(stderr,
"\n ");
2267 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i+(
int) nterms],
2270 (void) FormatLocaleFile(stderr,
";\n %s' \\\n", lookup);
2275 (void) FormatLocaleFile(stderr,
"Arc Distort, Internal Coefficients:\n");
2276 for (i=0; i < 5; i++)
2277 (
void) FormatLocaleFile(stderr,
2278 " c%.20g = %+lf\n",(
double) i,coeff[i]);
2279 (void) FormatLocaleFile(stderr,
"Arc Distort, FX Equivalent:\n");
2280 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2281 (void) FormatLocaleFile(stderr,
" -fx 'ii=i+page.x; jj=j+page.y;\n");
2282 (void) FormatLocaleFile(stderr,
" xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
2284 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2285 (void) FormatLocaleFile(stderr,
" xx=xx*%lf %+lf;\n",coeff[1],
2287 (void) FormatLocaleFile(stderr,
2288 " yy=(%lf - hypot(ii,jj)) * %lf;\n",coeff[2],coeff[3]);
2289 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2292 case PolarDistortion:
2294 (void) FormatLocaleFile(stderr,
"Polar Distort, Internal Coefficients\n");
2295 for (i=0; i < 8; i++)
2296 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2298 (void) FormatLocaleFile(stderr,
"Polar Distort, FX Equivalent:\n");
2299 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2300 (void) FormatLocaleFile(stderr,
2301 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",-coeff[2],-coeff[3]);
2302 (void) FormatLocaleFile(stderr,
" xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
2303 -(coeff[4]+coeff[5])/2 );
2304 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2305 (void) FormatLocaleFile(stderr,
" xx=xx*2*pi*%lf + v.w/2;\n",
2307 (void) FormatLocaleFile(stderr,
" yy=(hypot(ii,jj)%+lf)*%lf;\n",
2308 -coeff[1],coeff[7] );
2309 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2312 case DePolarDistortion:
2314 (void) FormatLocaleFile(stderr,
2315 "DePolar Distort, Internal Coefficients\n");
2316 for (i=0; i < 8; i++)
2317 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2319 (void) FormatLocaleFile(stderr,
"DePolar Distort, FX Equivalent:\n");
2320 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2321 (void) FormatLocaleFile(stderr,
" -fx 'aa=(i+.5)*%lf %+lf;\n",
2322 coeff[6],+coeff[4]);
2323 (void) FormatLocaleFile(stderr,
" rr=(j+.5)*%lf %+lf;\n",
2324 coeff[7],+coeff[1]);
2325 (void) FormatLocaleFile(stderr,
" xx=rr*sin(aa) %+lf;\n",
2327 (void) FormatLocaleFile(stderr,
" yy=rr*cos(aa) %+lf;\n",
2329 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2332 case Cylinder2PlaneDistortion:
2334 (void) FormatLocaleFile(stderr,
2335 "Cylinder to Plane Distort, Internal Coefficients\n");
2336 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2337 (void) FormatLocaleFile(stderr,
2338 "Cylinder to Plane Distort, FX Equivalent:\n");
2339 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2340 (void) FormatLocaleFile(stderr,
2341 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2343 (void) FormatLocaleFile(stderr,
" aa=atan(ii/%+lf);\n",coeff[1]);
2344 (void) FormatLocaleFile(stderr,
" xx=%lf*aa%+lf;\n",
2346 (void) FormatLocaleFile(stderr,
" yy=jj*cos(aa)%+lf;\n",coeff[3]);
2347 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2350 case Plane2CylinderDistortion:
2352 (void) FormatLocaleFile(stderr,
2353 "Plane to Cylinder Distort, Internal Coefficients\n");
2354 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2355 (void) FormatLocaleFile(stderr,
2356 "Plane to Cylinder Distort, FX Equivalent:\n");
2357 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2358 (void) FormatLocaleFile(stderr,
2359 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2361 (void) FormatLocaleFile(stderr,
" ii=ii/%+lf;\n",coeff[1]);
2362 (void) FormatLocaleFile(stderr,
" xx=%lf*tan(ii)%+lf;\n",coeff[1],
2364 (void) FormatLocaleFile(stderr,
" yy=jj/cos(ii)%+lf;\n",coeff[3]);
2365 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2368 case BarrelDistortion:
2369 case BarrelInverseDistortion:
2380 xc=((double)image->columns-1.0)/2.0+image->page.x;
2381 yc=((double)image->rows-1.0)/2.0+image->page.y;
2382 (void) FormatLocaleFile(stderr,
"Barrel%s Distort, FX Equivalent:\n",
2383 method == BarrelDistortion ?
"" :
"Inv");
2384 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2385 if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
2386 (void) FormatLocaleFile(stderr,
" -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
2388 (
void) FormatLocaleFile(stderr,
" -fx 'xc=%lf; yc=%lf;\n",coeff[8]-
2390 (void) FormatLocaleFile(stderr,
2391 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
2392 (void) FormatLocaleFile(stderr,
2393 " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2394 method == BarrelDistortion ?
"*" :
"/",coeff[0],coeff[1],coeff[2],
2396 (void) FormatLocaleFile(stderr,
2397 " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2398 method == BarrelDistortion ?
"*" :
"/",coeff[4],coeff[5],coeff[6],
2400 (void) FormatLocaleFile(stderr,
" p{ii+xc,jj+yc}' \\\n");
2412 {
const char *artifact;
2413 artifact=GetImageArtifact(image,
"distort:scale");
2414 output_scaling = 1.0;
2415 if (artifact != (
const char *) NULL) {
2416 output_scaling = fabs(StringToDouble(artifact,(
char **) NULL));
2417 geometry.width=CastDoubleToSizeT(output_scaling*geometry.width+0.5);
2418 geometry.height=CastDoubleToSizeT(output_scaling*geometry.height+0.5);
2419 geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
2420 geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
2421 if ( output_scaling < 0.1 ) {
2422 coeff = (
double *) RelinquishMagickMemory(coeff);
2423 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2424 "InvalidArgument",
"%s",
"-set option:distort:scale" );
2425 return((Image *) NULL);
2427 output_scaling = 1/output_scaling;
2430#define ScaleFilter(F,A,B,C,D) \
2431 ScaleResampleFilter( (F), \
2432 output_scaling*(A), output_scaling*(B), \
2433 output_scaling*(C), output_scaling*(D) )
2438 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
2440 if (distort_image == (Image *) NULL)
2442 coeff=(
double *) RelinquishMagickMemory(coeff);
2443 return((Image *) NULL);
2446 if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
2448 coeff=(
double *) RelinquishMagickMemory(coeff);
2449 distort_image=DestroyImage(distort_image);
2450 return((Image *) NULL);
2452 if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
2453 (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
2454 (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
2455 if (distort_image->background_color.alpha_trait != UndefinedPixelTrait)
2456 distort_image->alpha_trait=BlendPixelTrait;
2457 distort_image->page.x=geometry.x;
2458 distort_image->page.y=geometry.y;
2459 ConformPixelInfo(distort_image,&distort_image->matte_color,&invalid,
2478 **magick_restrict resample_filter;
2485 GetPixelInfo(distort_image,&zero);
2486 resample_filter=AcquireResampleFilterTLS(image,UndefinedVirtualPixelMethod,
2487 MagickFalse,exception);
2488 distort_view=AcquireAuthenticCacheView(distort_image,exception);
2489#if defined(MAGICKCORE_OPENMP_SUPPORT)
2490 #pragma omp parallel for schedule(static) shared(progress,status) \
2491 magick_number_threads(image,distort_image,distort_image->rows,1)
2493 for (j=0; j < (ssize_t) distort_image->rows; j++)
2496 id = GetOpenMPThreadId();
2517 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
2519 if (q == (Quantum *) NULL)
2531 case AffineDistortion:
2532 case RigidAffineDistortion:
2533 ScaleFilter( resample_filter[
id],
2535 coeff[3], coeff[4] );
2548 for (i=0; i < (ssize_t) distort_image->columns; i++)
2551 d.x = (double) (geometry.x+i+0.5)*output_scaling;
2552 d.y = (double) (geometry.y+j+0.5)*output_scaling;
2556 case AffineDistortion:
2557 case RigidAffineDistortion:
2559 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2560 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2564 case PerspectiveDistortion:
2567 p,n,r,abs_r,abs_c6,abs_c7,scale;
2569 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2570 n=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2571 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2573 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2576 abs_c6 = fabs(coeff[6]);
2577 abs_c7 = fabs(coeff[7]);
2578 if ( abs_c6 > abs_c7 ) {
2579 if ( abs_r < abs_c6*output_scaling )
2580 validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
2582 else if ( abs_r < abs_c7*output_scaling )
2583 validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
2585 if ( validity > 0.0 ) {
2592 ScaleFilter( resample_filter[
id],
2593 (r*coeff[0] - p*coeff[6])*scale,
2594 (r*coeff[1] - p*coeff[7])*scale,
2595 (r*coeff[3] - n*coeff[6])*scale,
2596 (r*coeff[4] - n*coeff[7])*scale );
2600 case BilinearReverseDistortion:
2603 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2604 s.y=coeff[4]*d.x+coeff[5]*d.y
2605 +coeff[6]*d.x*d.y+coeff[7];
2607 ScaleFilter( resample_filter[
id],
2608 coeff[0] + coeff[2]*d.y,
2609 coeff[1] + coeff[2]*d.x,
2610 coeff[4] + coeff[6]*d.y,
2611 coeff[5] + coeff[6]*d.x );
2614 case BilinearForwardDistortion:
2619 d.x -= coeff[3]; d.y -= coeff[7];
2620 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2621 c = coeff[4]*d.x - coeff[0]*d.y;
2626 if ( fabs(coeff[9]) < MagickEpsilon )
2629 c = b*b - 2*coeff[9]*c;
2633 s.y = ( -b + sqrt(c) )/coeff[9];
2635 if ( validity > 0.0 )
2636 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2646 case BilinearDistortion:
2651 case PolynomialDistortion:
2658 nterms=(ssize_t)coeff[1];
2663 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2664 for(k=0; k < nterms; k++) {
2665 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2666 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2667 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2668 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2669 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2670 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2672 ScaleFilter( resample_filter[
id], du.x,du.y,dv.x,dv.y );
2678 s.x = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
2679 s.x -= MagickRound(s.x);
2680 s.y = hypot(d.x,d.y);
2687 if ( s.y > MagickEpsilon )
2688 ScaleFilter( resample_filter[
id],
2689 (
double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
2691 ScaleFilter( resample_filter[
id],
2692 distort_image->columns*2, 0, 0, coeff[3] );
2695 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2696 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2699 case PolarDistortion:
2703 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2705 s.x -= MagickRound(s.x);
2707 s.y = hypot(d.x,d.y);
2712 if ( s.y > MagickEpsilon )
2713 ScaleFilter( resample_filter[
id],
2714 (
double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
2716 ScaleFilter( resample_filter[
id],
2717 distort_image->columns*2, 0, 0, coeff[7] );
2720 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2721 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2724 case DePolarDistortion:
2727 d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
2728 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2729 s.x = d.y*sin(d.x) + coeff[2];
2730 s.y = d.y*cos(d.x) + coeff[3];
2734 case Cylinder2PlaneDistortion:
2738 d.x -= coeff[4]; d.y -= coeff[5];
2745 ScaleFilter( resample_filter[
id],
2746 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2748if ( i == 0 && j == 0 ) {
2749 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2750 fprintf(stderr,
"phi = %lf\n", (
double)(ax * 180.0/MagickPI) );
2751 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2752 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2756 s.x += coeff[2]; s.y += coeff[3];
2759 case Plane2CylinderDistortion:
2762 d.x -= coeff[4]; d.y -= coeff[5];
2766 validity = (double) (coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5;
2768 if ( validity > 0.0 ) {
2776 ScaleFilter( resample_filter[
id],
2777 cx*cx, 0.0, s.y*cx/coeff[1], cx );
2780if ( d.x == 0.5 && d.y == 0.5 ) {
2781 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2782 fprintf(stderr,
"radius = %lf phi = %lf validity = %lf\n",
2783 coeff[1], (
double)(d.x * 180.0/MagickPI), validity );
2784 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2785 cx*cx, 0.0, s.y*cx/coeff[1], cx);
2790 s.x += coeff[2]; s.y += coeff[3];
2793 case BarrelDistortion:
2794 case BarrelInverseDistortion:
2796 double r,fx,fy,gx,gy;
2800 r = sqrt(d.x*d.x+d.y*d.y);
2801 if ( r > MagickEpsilon ) {
2802 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2803 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2804 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2805 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2807 if ( method == BarrelInverseDistortion ) {
2808 fx = 1/fx; fy = 1/fy;
2809 gx *= -fx*fx; gy *= -fy*fy;
2812 s.x = d.x*fx + coeff[8];
2813 s.y = d.y*fy + coeff[9];
2814 ScaleFilter( resample_filter[
id],
2815 gx*d.x*d.x + fx, gx*d.x*d.y,
2816 gy*d.x*d.y, gy*d.y*d.y + fy );
2825 if ( method == BarrelDistortion )
2826 ScaleFilter( resample_filter[
id],
2827 coeff[3], 0, 0, coeff[7] );
2830 ScaleFilter( resample_filter[
id],
2831 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2835 case ShepardsDistortion:
2851 denominator = s.x = s.y = 0;
2852 for(k=0; k<number_arguments; k+=4) {
2854 ((double)d.x-arguments[k+2])*((
double)d.x-arguments[k+2])
2855 + ((double)d.y-arguments[k+3])*((
double)d.y-arguments[k+3]);
2856 weight = pow(weight,coeff[0]);
2857 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
2859 s.x += (arguments[ k ]-arguments[k+2])*weight;
2860 s.y += (arguments[k+1]-arguments[k+3])*weight;
2861 denominator += weight;
2873 if ( bestfit && method != ArcDistortion ) {
2874 s.x -= image->page.x;
2875 s.y -= image->page.y;
2880 if ( validity <= 0.0 ) {
2882 SetPixelViaPixelInfo(distort_image,&invalid,q);
2886 status=ResamplePixelColor(resample_filter[
id],s.x,s.y,&pixel,
2888 if (status == MagickFalse)
2889 SetPixelViaPixelInfo(distort_image,&invalid,q);
2893 if ( validity < 1.0 ) {
2896 CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
2899 SetPixelViaPixelInfo(distort_image,&pixel,q);
2902 q+=(ptrdiff_t) GetPixelChannels(distort_image);
2904 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2905 if (sync == MagickFalse)
2907 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2912#if defined(MAGICKCORE_OPENMP_SUPPORT)
2916 proceed=SetImageProgress(image,DistortImageTag,progress,image->rows);
2917 if (proceed == MagickFalse)
2921 distort_view=DestroyCacheView(distort_view);
2922 resample_filter=DestroyResampleFilterTLS(resample_filter);
2924 if (status == MagickFalse)
2925 distort_image=DestroyImage(distort_image);
2931 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2932 distort_image->page.x = 0;
2933 distort_image->page.y = 0;
2935 coeff=(
double *) RelinquishMagickMemory(coeff);
2936 return(distort_image);
2973MagickExport Image *RotateImage(
const Image *image,
const double degrees,
2974 ExceptionInfo *exception)
2992 assert(image != (Image *) NULL);
2993 assert(image->signature == MagickCoreSignature);
2994 if (IsEventLogging() != MagickFalse)
2995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2996 assert(exception != (ExceptionInfo *) NULL);
2997 assert(exception->signature == MagickCoreSignature);
2998 angle=fmod(degrees,360.0);
2999 while (angle < -45.0)
3001 for (rotations=0; angle > 45.0; rotations++)
3004 shear.x=(-tan((
double) DegreesToRadians(angle)/2.0));
3005 shear.y=sin((
double) DegreesToRadians(angle));
3006 if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
3007 return(IntegralRotateImage(image,rotations,exception));
3008 distort_image=CloneImage(image,0,0,MagickTrue,exception);
3009 if (distort_image == (Image *) NULL)
3010 return((Image *) NULL);
3011 (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
3013 rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
3014 °rees,MagickTrue,exception);
3015 distort_image=DestroyImage(distort_image);
3016 return(rotate_image);
3058MagickExport Image *SparseColorImage(
const Image *image,
3059 const SparseColorMethod method,
const size_t number_arguments,
3060 const double *arguments,ExceptionInfo *exception)
3062#define SparseColorTag "Distort/SparseColor"
3076 assert(image != (Image *) NULL);
3077 assert(image->signature == MagickCoreSignature);
3078 assert(exception != (ExceptionInfo *) NULL);
3079 assert(exception->signature == MagickCoreSignature);
3080 if (IsEventLogging() != MagickFalse)
3081 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3085 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3087 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3089 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3091 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3092 (image->colorspace == CMYKColorspace))
3094 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3095 (image->alpha_trait != UndefinedPixelTrait))
3105 distort_method=(DistortMethod) method;
3106 if ( distort_method >= SentinelDistortion )
3107 distort_method = ShepardsDistortion;
3108 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
3109 arguments, number_colors, exception);
3110 if ( coeff == (
double *) NULL )
3111 return((Image *) NULL);
3118 sparse_method = (SparseColorMethod) distort_method;
3119 if ( distort_method == ShepardsDistortion )
3120 sparse_method = method;
3121 if ( sparse_method == InverseColorInterpolate )
3126 if (IsStringTrue(GetImageArtifact(image,
"verbose")) != MagickFalse) {
3128 switch (sparse_method) {
3129 case BarycentricColorInterpolate:
3132 (void) FormatLocaleFile(stderr,
"Barycentric Sparse Color:\n");
3133 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3134 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
3135 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3136 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3137 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
3138 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3139 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3140 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
3141 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3142 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3143 (image->colorspace == CMYKColorspace))
3144 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
3145 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3146 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3147 (image->alpha_trait != UndefinedPixelTrait))
3148 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
3149 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3152 case BilinearColorInterpolate:
3155 (void) FormatLocaleFile(stderr,
"Bilinear Sparse Color\n");
3156 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3157 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3158 coeff[ x ], coeff[x+1],
3159 coeff[x+2], coeff[x+3]),x+=4;
3160 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3161 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3162 coeff[ x ], coeff[x+1],
3163 coeff[x+2], coeff[x+3]),x+=4;
3164 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3165 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3166 coeff[ x ], coeff[x+1],
3167 coeff[x+2], coeff[x+3]),x+=4;
3168 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3169 (image->colorspace == CMYKColorspace))
3170 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3171 coeff[ x ], coeff[x+1],
3172 coeff[x+2], coeff[x+3]),x+=4;
3173 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3174 (image->alpha_trait != UndefinedPixelTrait))
3175 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3176 coeff[ x ], coeff[x+1],
3177 coeff[x+2], coeff[x+3]),x+=4;
3192 sparse_image=CloneImage(image,0,0,MagickTrue,exception);
3193 if (sparse_image == (Image *) NULL)
3194 return((Image *) NULL);
3195 if (SetImageStorageClass(sparse_image,DirectClass,exception) == MagickFalse)
3197 sparse_image=DestroyImage(sparse_image);
3198 return((Image *) NULL);
3200 if (IsGrayColorspace(sparse_image->colorspace) != MagickFalse)
3201 (void) SetImageColorspace(sparse_image,sRGBColorspace,exception);
3217 sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
3218#if defined(MAGICKCORE_OPENMP_SUPPORT)
3219 #pragma omp parallel for schedule(static) shared(progress,status) \
3220 magick_number_threads(image,sparse_image,sparse_image->rows,1)
3222 for (j=0; j < (ssize_t) sparse_image->rows; j++)
3236 q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,1,
3238 if (q == (Quantum *) NULL)
3243 GetPixelInfo(sparse_image,&pixel);
3244 for (i=0; i < (ssize_t) sparse_image->columns; i++)
3246 GetPixelInfoPixel(sparse_image,q,&pixel);
3247 switch (sparse_method)
3249 case BarycentricColorInterpolate:
3252 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3253 pixel.red = coeff[x]*i +coeff[x+1]*j
3255 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3256 pixel.green = coeff[x]*i +coeff[x+1]*j
3258 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3259 pixel.blue = coeff[x]*i +coeff[x+1]*j
3261 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3262 (sparse_image->colorspace == CMYKColorspace))
3263 pixel.black = coeff[x]*i +coeff[x+1]*j
3265 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3266 (sparse_image->alpha_trait != UndefinedPixelTrait))
3267 pixel.alpha = coeff[x]*i +coeff[x+1]*j
3271 case BilinearColorInterpolate:
3274 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3275 pixel.red = coeff[x]*i + coeff[x+1]*j +
3276 coeff[x+2]*i*j + coeff[x+3], x+=4;
3277 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3278 pixel.green = coeff[x]*i + coeff[x+1]*j +
3279 coeff[x+2]*i*j + coeff[x+3], x+=4;
3280 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3281 pixel.blue = coeff[x]*i + coeff[x+1]*j +
3282 coeff[x+2]*i*j + coeff[x+3], x+=4;
3283 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3284 (image->colorspace == CMYKColorspace))
3285 pixel.black = coeff[x]*i + coeff[x+1]*j +
3286 coeff[x+2]*i*j + coeff[x+3], x+=4;
3287 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3288 (sparse_image->alpha_trait != UndefinedPixelTrait))
3289 pixel.alpha = coeff[x]*i + coeff[x+1]*j +
3290 coeff[x+2]*i*j + coeff[x+3], x+=4;
3293 case InverseColorInterpolate:
3294 case ShepardsColorInterpolate:
3302 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3304 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3306 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3308 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3309 (image->colorspace == CMYKColorspace))
3311 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3312 (sparse_image->alpha_trait != UndefinedPixelTrait))
3315 for (k=0; k<number_arguments; k+=2+number_colors)
3318 ((double) i-arguments[ k ])*((
double) i-arguments[ k ])
3319 + ((double) j-arguments[k+1])*((
double) j-arguments[k+1]);
3320 ssize_t x = (ssize_t) k+2;
3322 weight = pow(weight,coeff[0]);
3323 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
3324 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3325 pixel.red += arguments[x++]*weight;
3326 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3327 pixel.green += arguments[x++]*weight;
3328 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3329 pixel.blue += arguments[x++]*weight;
3330 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3331 (image->colorspace == CMYKColorspace))
3332 pixel.black += arguments[x++]*weight;
3333 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3334 (sparse_image->alpha_trait != UndefinedPixelTrait))
3335 pixel.alpha += arguments[x++]*weight;
3336 denominator += weight;
3338 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3339 pixel.red/=denominator;
3340 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3341 pixel.green/=denominator;
3342 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3343 pixel.blue/=denominator;
3344 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3345 (image->colorspace == CMYKColorspace))
3346 pixel.black/=denominator;
3347 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3348 (sparse_image->alpha_trait != UndefinedPixelTrait))
3349 pixel.alpha/=denominator;
3352 case ManhattanColorInterpolate:
3355 minimum = MagickMaximumValue;
3363 for (k=0; k<number_arguments; k+=2+number_colors)
3365 double distance = fabs((
double)i-arguments[ k ])+
3366 fabs((
double)j-arguments[k+1]);
3367 if ( distance < minimum ) {
3368 ssize_t x=(ssize_t) k+2;
3369 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3370 pixel.red=arguments[x++];
3371 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3372 pixel.green=arguments[x++];
3373 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3374 pixel.blue=arguments[x++];
3375 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3376 (image->colorspace == CMYKColorspace))
3377 pixel.black=arguments[x++];
3378 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3379 (sparse_image->alpha_trait != UndefinedPixelTrait))
3380 pixel.alpha=arguments[x++];
3386 case VoronoiColorInterpolate:
3390 minimum = MagickMaximumValue;
3398 for (k=0; k<number_arguments; k+=2+number_colors) {
3400 ((double) i-arguments[ k ])*((
double) i-arguments[ k ])
3401 + ((double) j-arguments[k+1])*((
double) j-arguments[k+1]);
3402 if ( distance < minimum ) {
3403 ssize_t x = (ssize_t) k+2;
3404 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3405 pixel.red=arguments[x++];
3406 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3407 pixel.green=arguments[x++];
3408 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3409 pixel.blue=arguments[x++];
3410 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3411 (image->colorspace == CMYKColorspace))
3412 pixel.black=arguments[x++];
3413 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3414 (sparse_image->alpha_trait != UndefinedPixelTrait))
3415 pixel.alpha=arguments[x++];
3423 if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3424 pixel.red=(MagickRealType) ClampPixel((
double) QuantumRange*
3426 if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3427 pixel.green=(MagickRealType) ClampPixel((
double) QuantumRange*
3429 if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3430 pixel.blue=(MagickRealType) ClampPixel((
double) QuantumRange*
3432 if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3433 (image->colorspace == CMYKColorspace))
3434 pixel.black=(MagickRealType) ClampPixel((
double) QuantumRange*
3436 if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3437 (image->alpha_trait != UndefinedPixelTrait))
3438 pixel.alpha=(MagickRealType) ClampPixel((
double) QuantumRange*
3440 SetPixelViaPixelInfo(sparse_image,&pixel,q);
3441 q+=(ptrdiff_t) GetPixelChannels(sparse_image);
3443 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
3444 if (sync == MagickFalse)
3446 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3451#if defined(MAGICKCORE_OPENMP_SUPPORT)
3455 proceed=SetImageProgress(image,SparseColorTag,progress,image->rows);
3456 if (proceed == MagickFalse)
3460 sparse_view=DestroyCacheView(sparse_view);
3461 if (status == MagickFalse)
3462 sparse_image=DestroyImage(sparse_image);
3464 coeff = (
double *) RelinquishMagickMemory(coeff);
3465 return(sparse_image);