00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/artifact.h"
00045 #include "magick/cache.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/colorspace-private.h"
00048 #include "magick/composite-private.h"
00049 #include "magick/distort.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/gem.h"
00053 #include "magick/hashmap.h"
00054 #include "magick/image.h"
00055 #include "magick/list.h"
00056 #include "magick/matrix.h"
00057 #include "magick/memory_.h"
00058 #include "magick/monitor-private.h"
00059 #include "magick/pixel.h"
00060 #include "magick/pixel-private.h"
00061 #include "magick/resample.h"
00062 #include "magick/resample-private.h"
00063 #include "magick/registry.h"
00064 #include "magick/semaphore.h"
00065 #include "magick/string_.h"
00066 #include "magick/string-private.h"
00067 #include "magick/thread-private.h"
00068 #include "magick/token.h"
00069
00070
00071
00072
00073 static inline double MagickMin(const double x,const double y)
00074 {
00075 return( x < y ? x : y);
00076 }
00077 static inline double MagickMax(const double x,const double y)
00078 {
00079 return( x > y ? x : y);
00080 }
00081
00082 static inline void AffineArgsToCoefficients(double *affine)
00083 {
00084
00085 double tmp[4];
00086 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
00087 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
00088 }
00089
00090 static inline void CoefficientsToAffineArgs(double *coeff)
00091 {
00092
00093 double tmp[4];
00094 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
00095 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
00096 }
00097 static void InvertAffineCoefficients(const double *coeff,double *inverse)
00098 {
00099
00100 double determinant;
00101
00102 determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
00103 inverse[0]=determinant*coeff[4];
00104 inverse[1]=determinant*(-coeff[1]);
00105 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
00106 inverse[3]=determinant*(-coeff[3]);
00107 inverse[4]=determinant*coeff[0];
00108 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
00109 }
00110
00111 static void InvertPerspectiveCoefficients(const double *coeff,
00112 double *inverse)
00113 {
00114
00115 double determinant;
00116
00117 determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
00118 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
00119 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
00120 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
00121 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
00122 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
00123 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
00124 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
00125 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
00126 }
00127
00128 static inline double MagickRound(double x)
00129 {
00130
00131
00132
00133 if (x >= 0.0)
00134 return((double) ((long) (x+0.5)));
00135 return((double) ((long) (x-0.5)));
00136 }
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 static unsigned long poly_number_terms(double order)
00158 {
00159
00160 if ( order < 1 || order > 5 ||
00161 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
00162 return 0;
00163 return((unsigned long) floor((order+1)*(order+2)/2));
00164 }
00165
00166 static double poly_basis_fn(long n, double x, double y)
00167 {
00168
00169 switch(n) {
00170 case 0: return( 1.0 );
00171 case 1: return( x );
00172 case 2: return( y );
00173 case 3: return( x*y );
00174 case 4: return( x*x );
00175 case 5: return( y*y );
00176 case 6: return( x*x*x );
00177 case 7: return( x*x*y );
00178 case 8: return( x*y*y );
00179 case 9: return( y*y*y );
00180 case 10: return( x*x*x*x );
00181 case 11: return( x*x*x*y );
00182 case 12: return( x*x*y*y );
00183 case 13: return( x*y*y*y );
00184 case 14: return( y*y*y*y );
00185 case 15: return( x*x*x*x*x );
00186 case 16: return( x*x*x*x*y );
00187 case 17: return( x*x*x*y*y );
00188 case 18: return( x*x*y*y*y );
00189 case 19: return( x*y*y*y*y );
00190 case 20: return( y*y*y*y*y );
00191 }
00192 return( 0 );
00193 }
00194 static const char *poly_basis_str(long n)
00195 {
00196
00197 switch(n) {
00198 case 0: return("");
00199 case 1: return("*ii");
00200 case 2: return("*jj");
00201 case 3: return("*ii*jj");
00202 case 4: return("*ii*ii");
00203 case 5: return("*jj*jj");
00204 case 6: return("*ii*ii*ii");
00205 case 7: return("*ii*ii*jj");
00206 case 8: return("*ii*jj*jj");
00207 case 9: return("*jj*jj*jj");
00208 case 10: return("*ii*ii*ii*ii");
00209 case 11: return("*ii*ii*ii*jj");
00210 case 12: return("*ii*ii*jj*jj");
00211 case 13: return("*ii*jj*jj*jj");
00212 case 14: return("*jj*jj*jj*jj");
00213 case 15: return("*ii*ii*ii*ii*ii");
00214 case 16: return("*ii*ii*ii*ii*jj");
00215 case 17: return("*ii*ii*ii*jj*jj");
00216 case 18: return("*ii*ii*jj*jj*jj");
00217 case 19: return("*ii*jj*jj*jj*jj");
00218 case 20: return("*jj*jj*jj*jj*jj");
00219 }
00220 return( "UNKNOWN" );
00221 }
00222 static double poly_basis_dx(long n, double x, double y)
00223 {
00224
00225 switch(n) {
00226 case 0: return( 0.0 );
00227 case 1: return( 1.0 );
00228 case 2: return( 0.0 );
00229 case 3: return( y );
00230 case 4: return( x );
00231 case 5: return( 0.0 );
00232 case 6: return( x*x );
00233 case 7: return( x*y );
00234 case 8: return( y*y );
00235 case 9: return( 0.0 );
00236 case 10: return( x*x*x );
00237 case 11: return( x*x*y );
00238 case 12: return( x*y*y );
00239 case 13: return( y*y*y );
00240 case 14: return( 0.0 );
00241 case 15: return( x*x*x*x );
00242 case 16: return( x*x*x*y );
00243 case 17: return( x*x*y*y );
00244 case 18: return( x*y*y*y );
00245 case 19: return( y*y*y*y );
00246 case 20: return( 0.0 );
00247 }
00248 return( 0.0 );
00249 }
00250 static double poly_basis_dy(long n, double x, double y)
00251 {
00252
00253 switch(n) {
00254 case 0: return( 0.0 );
00255 case 1: return( 0.0 );
00256 case 2: return( 1.0 );
00257 case 3: return( x );
00258 case 4: return( 0.0 );
00259 case 5: return( y );
00260 default: return( poly_basis_dx(n-1,x,y) );
00261 }
00262
00263
00264
00265 }
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317 static double *GenerateCoefficients(const Image *image,
00318 DistortImageMethod *method,const unsigned long number_arguments,
00319 const double *arguments,unsigned long number_values,ExceptionInfo *exception)
00320 {
00321 double
00322 *coeff;
00323
00324 register unsigned long
00325 i;
00326
00327 unsigned long
00328 number_coeff,
00329 cp_size,
00330 cp_x,cp_y,
00331 cp_values;
00332
00333
00334 if ( number_values == 0 ) {
00335
00336
00337
00338 number_values = 2;
00339 cp_values = 0;
00340 cp_x = 2;
00341 cp_y = 3;
00342
00343 }
00344 else {
00345 cp_x = 0;
00346 cp_y = 1;
00347 cp_values = 2;
00348
00349 }
00350 cp_size = number_values+2;
00351
00352
00353
00354
00355 if ( number_arguments < 4*cp_size &&
00356 ( *method == BilinearForwardDistortion
00357 || *method == BilinearReverseDistortion
00358 || *method == PerspectiveDistortion
00359 ) )
00360 *method = AffineDistortion;
00361
00362 number_coeff=0;
00363 switch (*method) {
00364 case AffineDistortion:
00365
00366 number_coeff=3*number_values;
00367 break;
00368 case PolynomialDistortion:
00369
00370 if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
00371 {
00372 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00373 "InvalidArgument","%s : '%s'","Polynomial",
00374 "Invalid number of args: order [CPs]...");
00375 return((double *) NULL);
00376 }
00377 i = poly_number_terms(arguments[0]);
00378 number_coeff = 2 + i*number_values;
00379 if ( i == 0 ) {
00380 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00381 "InvalidArgument","%s : '%s'","Polynomial",
00382 "Invalid order, should be interger 1 to 5, or 1.5");
00383 return((double *) NULL);
00384 }
00385 if ( number_arguments < 1+i*cp_size ) {
00386 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00387 "InvalidArgument", "%s : 'require at least %ld CPs'",
00388 "Polynomial", i);
00389 return((double *) NULL);
00390 }
00391 break;
00392 case BilinearReverseDistortion:
00393 number_coeff=4*number_values;
00394 break;
00395
00396
00397
00398 case BilinearForwardDistortion:
00399 number_coeff=10;
00400 cp_x = 0;
00401 cp_y = 1;
00402 cp_values = 2;
00403 break;
00404 #if 0
00405 case QuadraterialDistortion:
00406 number_coeff=19;
00407 #endif
00408 break;
00409 case ShepardsDistortion:
00410 case VoronoiColorInterpolate:
00411 number_coeff=1;
00412 break;
00413 case ArcDistortion:
00414 number_coeff=5;
00415 break;
00416 case ScaleRotateTranslateDistortion:
00417 case AffineProjectionDistortion:
00418 number_coeff=6;
00419 break;
00420 case PolarDistortion:
00421 case DePolarDistortion:
00422 number_coeff=8;
00423 break;
00424 case PerspectiveDistortion:
00425 case PerspectiveProjectionDistortion:
00426 number_coeff=9;
00427 break;
00428 case BarrelDistortion:
00429 case BarrelInverseDistortion:
00430 number_coeff=10;
00431 break;
00432 case UndefinedDistortion:
00433 default:
00434 assert(! "Unknown Method Given");
00435 }
00436
00437
00438 coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
00439 if (coeff == (double *) NULL) {
00440 (void) ThrowMagickException(exception,GetMagickModule(),
00441 ResourceLimitError,"MemoryAllocationFailed",
00442 "%s", "GenerateCoefficients");
00443 return((double *) NULL);
00444 }
00445
00446
00447 for (i=0; i < number_coeff; i++)
00448 coeff[i] = 0.0;
00449
00450 switch (*method)
00451 {
00452 case AffineDistortion:
00453 {
00454
00455
00456
00457
00458
00459
00460
00461
00462 if ( number_arguments%cp_size != 0 ||
00463 number_arguments < cp_size ) {
00464 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00465 "InvalidArgument", "%s : 'require at least %ld CPs'",
00466 "Affine", 1L);
00467 coeff=(double *) RelinquishMagickMemory(coeff);
00468 return((double *) NULL);
00469 }
00470
00471 if ( number_arguments == cp_size ) {
00472
00473 if ( cp_values == 0 ) {
00474
00475 coeff[0] = 1.0;
00476 coeff[2] = arguments[0] - arguments[2];
00477 coeff[4] = 1.0;
00478 coeff[5] = arguments[1] - arguments[3];
00479 }
00480 else {
00481
00482 for (i=0; i<number_values; i++)
00483 coeff[i*3+2] = arguments[cp_values+i];
00484 }
00485 }
00486 else {
00487
00488
00489
00490 double
00491 **matrix,
00492 **vectors,
00493 terms[3];
00494
00495 MagickBooleanType
00496 status;
00497
00498
00499 matrix = AcquireMagickMatrix(3UL,3UL);
00500 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
00501 if (matrix == (double **) NULL || vectors == (double **) NULL)
00502 {
00503 matrix = RelinquishMagickMatrix(matrix, 3UL);
00504 vectors = (double **) RelinquishMagickMemory(vectors);
00505 coeff = (double *) RelinquishMagickMemory(coeff);
00506 (void) ThrowMagickException(exception,GetMagickModule(),
00507 ResourceLimitError,"MemoryAllocationFailed",
00508 "%s", "DistortCoefficients");
00509 return((double *) NULL);
00510 }
00511
00512 for (i=0; i < number_values; i++)
00513 vectors[i] = &(coeff[i*3]);
00514
00515 for (i=0; i < number_arguments; i+=cp_size) {
00516 terms[0] = arguments[i+cp_x];
00517 terms[1] = arguments[i+cp_y];
00518 terms[2] = 1;
00519 LeastSquaresAddTerms(matrix,vectors,terms,
00520 &(arguments[i+cp_values]),3UL,number_values);
00521 }
00522 if ( number_arguments == 2*cp_size ) {
00523
00524
00525
00526
00527 terms[0] = arguments[cp_x]
00528 - ( arguments[cp_size+cp_y] - arguments[cp_y] );
00529 terms[1] = arguments[cp_y] +
00530 + ( arguments[cp_size+cp_x] - arguments[cp_x] );
00531 terms[2] = 1;
00532 if ( cp_values == 0 ) {
00533
00534 double
00535 uv2[2];
00536 uv2[0] = arguments[0] - arguments[5] + arguments[1];
00537 uv2[1] = arguments[1] + arguments[4] - arguments[0];
00538 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
00539 }
00540 else {
00541
00542 LeastSquaresAddTerms(matrix,vectors,terms,
00543 &(arguments[cp_values]),3UL,number_values);
00544 }
00545 }
00546
00547 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
00548 matrix = RelinquishMagickMatrix(matrix, 3UL);
00549 vectors = (double **) RelinquishMagickMemory(vectors);
00550 if ( status == MagickFalse ) {
00551 coeff = (double *) RelinquishMagickMemory(coeff);
00552 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00553 "InvalidArgument","%s : '%s'","Affine",
00554 "Unsolvable Matrix");
00555 return((double *) NULL);
00556 }
00557 }
00558 return(coeff);
00559 }
00560 case AffineProjectionDistortion:
00561 {
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575 double inverse[8];
00576 if (number_arguments != 6) {
00577 coeff = (double *) RelinquishMagickMemory(coeff);
00578 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00579 "InvalidArgument","%s : '%s'","AffineProjection",
00580 "Needs 6 coeff values");
00581 return((double *) NULL);
00582 }
00583
00584 for(i=0; i<6UL; i++ )
00585 inverse[i] = arguments[i];
00586 AffineArgsToCoefficients(inverse);
00587 InvertAffineCoefficients(inverse, coeff);
00588 *method = AffineDistortion;
00589
00590 return(coeff);
00591 }
00592 case ScaleRotateTranslateDistortion:
00593 {
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616 double
00617 cosine, sine,
00618 x,y,sx,sy,a,nx,ny;
00619
00620
00621 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
00622 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
00623 sx = sy = 1.0;
00624 switch ( number_arguments ) {
00625 case 0:
00626 coeff = (double *) RelinquishMagickMemory(coeff);
00627 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00628 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00629 "Needs at least 1 argument");
00630 return((double *) NULL);
00631 case 1:
00632 a = arguments[0];
00633 break;
00634 case 2:
00635 sx = sy = arguments[0];
00636 a = arguments[1];
00637 break;
00638 default:
00639 x = nx = arguments[0];
00640 y = ny = arguments[1];
00641 switch ( number_arguments ) {
00642 case 3:
00643 a = arguments[2];
00644 break;
00645 case 4:
00646 sx = sy = arguments[2];
00647 a = arguments[3];
00648 break;
00649 case 5:
00650 sx = arguments[2];
00651 sy = arguments[3];
00652 a = arguments[4];
00653 break;
00654 case 6:
00655 sx = sy = arguments[2];
00656 a = arguments[3];
00657 nx = arguments[4];
00658 ny = arguments[5];
00659 break;
00660 case 7:
00661 sx = arguments[2];
00662 sy = arguments[3];
00663 a = arguments[4];
00664 nx = arguments[5];
00665 ny = arguments[6];
00666 break;
00667 default:
00668 coeff = (double *) RelinquishMagickMemory(coeff);
00669 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00670 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00671 "Too Many Arguments (7 or less)");
00672 return((double *) NULL);
00673 }
00674 break;
00675 }
00676
00677 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
00678 coeff = (double *) RelinquishMagickMemory(coeff);
00679 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00680 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00681 "Zero Scale Given");
00682 return((double *) NULL);
00683 }
00684
00685 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
00686
00687 *method = AffineDistortion;
00688 coeff[0]=cosine/sx;
00689 coeff[1]=sine/sx;
00690 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
00691 coeff[3]=(-sine)/sy;
00692 coeff[4]=cosine/sy;
00693 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
00694 return(coeff);
00695 }
00696 case PerspectiveDistortion:
00697 {
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727 double
00728 **matrix,
00729 *vectors[1],
00730 terms[8];
00731
00732 unsigned long
00733 cp_u = cp_values,
00734 cp_v = cp_values+1;
00735
00736 MagickBooleanType
00737 status;
00738
00739 if ( number_arguments%cp_size != 0 ||
00740 number_arguments < cp_size*4 ) {
00741 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00742 "InvalidArgument", "%s : 'require at least %ld CPs'",
00743 "Perspective", 4L);
00744 coeff=(double *) RelinquishMagickMemory(coeff);
00745 return((double *) NULL);
00746 }
00747
00748 vectors[0] = &(coeff[0]);
00749
00750 matrix = AcquireMagickMatrix(8UL,8UL);
00751 if (matrix == (double **) NULL) {
00752 (void) ThrowMagickException(exception,GetMagickModule(),
00753 ResourceLimitError,"MemoryAllocationFailed",
00754 "%s", "DistortCoefficients");
00755 return((double *) NULL);
00756 }
00757
00758 for (i=0; i < number_arguments; i+=4) {
00759 terms[0]=arguments[i+cp_x];
00760 terms[1]=arguments[i+cp_y];
00761 terms[2]=1.0;
00762 terms[3]=0.0;
00763 terms[4]=0.0;
00764 terms[5]=0.0;
00765 terms[6]=-terms[0]*arguments[i+cp_u];
00766 terms[7]=-terms[1]*arguments[i+cp_u];
00767 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
00768 8UL,1UL);
00769
00770 terms[0]=0.0;
00771 terms[1]=0.0;
00772 terms[2]=0.0;
00773 terms[3]=arguments[i+cp_x];
00774 terms[4]=arguments[i+cp_y];
00775 terms[5]=1.0;
00776 terms[6]=-terms[3]*arguments[i+cp_v];
00777 terms[7]=-terms[4]*arguments[i+cp_v];
00778 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
00779 8UL,1UL);
00780 }
00781
00782 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
00783 matrix = RelinquishMagickMatrix(matrix, 8UL);
00784 if ( status == MagickFalse ) {
00785 coeff = (double *) RelinquishMagickMemory(coeff);
00786 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00787 "InvalidArgument","%s : '%s'","Perspective",
00788 "Unsolvable Matrix");
00789 return((double *) NULL);
00790 }
00791
00792
00793
00794
00795
00796
00797 coeff[8] = coeff[6]*arguments[cp_x]
00798 + coeff[7]*arguments[cp_y] + 1.0;
00799 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
00800
00801 return(coeff);
00802 }
00803 case PerspectiveProjectionDistortion:
00804 {
00805
00806
00807
00808 if (number_arguments != 8) {
00809 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00810 "InvalidArgument","%s : '%s'","PerspectiveProjection",
00811 "Needs 8 coefficient values");
00812 return((double *) NULL);
00813 }
00814
00815 InvertPerspectiveCoefficients(arguments, coeff);
00816
00817
00818
00819
00820
00821
00822
00823 coeff[8] = coeff[6]*arguments[2]
00824 + coeff[7]*arguments[5] + 1.0;
00825 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
00826 *method = PerspectiveDistortion;
00827
00828 return(coeff);
00829 }
00830 case BilinearForwardDistortion:
00831 case BilinearReverseDistortion:
00832 {
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846 double
00847 **matrix,
00848 **vectors,
00849 terms[4];
00850
00851 MagickBooleanType
00852 status;
00853
00854
00855 if ( number_arguments%cp_size != 0 ||
00856 number_arguments < cp_size*4 ) {
00857 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00858 "InvalidArgument", "%s : 'require at least %ld CPs'",
00859 *method == BilinearForwardDistortion ? "BilinearForward" :
00860 "BilinearReverse", 4L);
00861 coeff=(double *) RelinquishMagickMemory(coeff);
00862 return((double *) NULL);
00863 }
00864
00865 matrix = AcquireMagickMatrix(4UL,4UL);
00866 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
00867 if (matrix == (double **) NULL || vectors == (double **) NULL)
00868 {
00869 matrix = RelinquishMagickMatrix(matrix, 4UL);
00870 vectors = (double **) RelinquishMagickMemory(vectors);
00871 coeff = (double *) RelinquishMagickMemory(coeff);
00872 (void) ThrowMagickException(exception,GetMagickModule(),
00873 ResourceLimitError,"MemoryAllocationFailed",
00874 "%s", "DistortCoefficients");
00875 return((double *) NULL);
00876 }
00877
00878 for (i=0; i < number_values; i++)
00879 vectors[i] = &(coeff[i*4]);
00880
00881 for (i=0; i < number_arguments; i+=cp_size) {
00882 terms[0] = arguments[i+cp_x];
00883 terms[1] = arguments[i+cp_y];
00884 terms[2] = terms[0]*terms[1];
00885 terms[3] = 1;
00886 LeastSquaresAddTerms(matrix,vectors,terms,
00887 &(arguments[i+cp_values]),4UL,number_values);
00888 }
00889
00890 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
00891 matrix = RelinquishMagickMatrix(matrix, 4UL);
00892 vectors = (double **) RelinquishMagickMemory(vectors);
00893 if ( status == MagickFalse ) {
00894 coeff = (double *) RelinquishMagickMemory(coeff);
00895 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00896 "InvalidArgument","%s : '%s'",
00897 *method == BilinearForwardDistortion ?
00898 "BilinearForward" : "BilinearReverse",
00899 "Unsolvable Matrix");
00900 return((double *) NULL);
00901 }
00902 if ( *method == BilinearForwardDistortion ) {
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
00943 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
00944 }
00945 return(coeff);
00946 }
00947 #if 0
00948 case QuadrilateralDistortion:
00949 {
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960 return(coeff);
00961 }
00962 #endif
00963
00964 case PolynomialDistortion:
00965 {
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987 double
00988 **matrix,
00989 **vectors,
00990 *terms;
00991
00992 unsigned long
00993 nterms;
00994
00995 register long
00996 j;
00997
00998 MagickBooleanType
00999 status;
01000
01001
01002 coeff[0] = arguments[0];
01003 coeff[1] = (double) poly_number_terms(arguments[0]);
01004 nterms = (unsigned long) coeff[1];
01005
01006
01007 matrix = AcquireMagickMatrix(nterms,nterms);
01008 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
01009 terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
01010 if (matrix == (double **) NULL ||
01011 vectors == (double **) NULL ||
01012 terms == (double *) NULL )
01013 {
01014 matrix = RelinquishMagickMatrix(matrix, nterms);
01015 vectors = (double **) RelinquishMagickMemory(vectors);
01016 terms = (double *) RelinquishMagickMemory(terms);
01017 coeff = (double *) RelinquishMagickMemory(coeff);
01018 (void) ThrowMagickException(exception,GetMagickModule(),
01019 ResourceLimitError,"MemoryAllocationFailed",
01020 "%s", "DistortCoefficients");
01021 return((double *) NULL);
01022 }
01023
01024 for (i=0; i < number_values; i++)
01025 vectors[i] = &(coeff[2+i*nterms]);
01026
01027 for (i=1; i < number_arguments; i+=cp_size) {
01028 for (j=0; j < (long) nterms; j++)
01029 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
01030 LeastSquaresAddTerms(matrix,vectors,terms,
01031 &(arguments[i+cp_values]),nterms,number_values);
01032 }
01033 terms = (double *) RelinquishMagickMemory(terms);
01034
01035 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
01036 matrix = RelinquishMagickMatrix(matrix, nterms);
01037 vectors = (double **) RelinquishMagickMemory(vectors);
01038 if ( status == MagickFalse ) {
01039 coeff = (double *) RelinquishMagickMemory(coeff);
01040 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01041 "InvalidArgument","%s : '%s'","Polynomial",
01042 "Unsolvable Matrix");
01043 return((double *) NULL);
01044 }
01045 return(coeff);
01046 }
01047 case ArcDistortion:
01048 {
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
01084 coeff = (double *) RelinquishMagickMemory(coeff);
01085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01086 "InvalidArgument","%s : '%s'", "Arc",
01087 "Arc Angle Too Small");
01088 return((double *) NULL);
01089 }
01090 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
01091 coeff = (double *) RelinquishMagickMemory(coeff);
01092 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01093 "InvalidArgument","%s : '%s'", "Arc",
01094 "Outer Radius Too Small");
01095 return((double *) NULL);
01096 }
01097 coeff[0] = -MagickPI2;
01098 if ( number_arguments >= 1 )
01099 coeff[1] = DegreesToRadians(arguments[0]);
01100 else
01101 coeff[1] = MagickPI2;
01102 if ( number_arguments >= 2 )
01103 coeff[0] += DegreesToRadians(arguments[1]);
01104 coeff[0] /= Magick2PI;
01105 coeff[0] -= MagickRound(coeff[0]);
01106 coeff[0] *= Magick2PI;
01107 coeff[3] = (double)image->rows-1;
01108 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
01109 if ( number_arguments >= 3 ) {
01110 if ( number_arguments >= 4 )
01111 coeff[3] = arguments[2] - arguments[3];
01112 else
01113 coeff[3] *= arguments[2]/coeff[2];
01114 coeff[2] = arguments[2];
01115 }
01116 coeff[4] = ((double)image->columns-1.0)/2.0;
01117
01118 return(coeff);
01119 }
01120 case PolarDistortion:
01121 case DePolarDistortion:
01122 {
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133 if ( number_arguments == 3
01134 || ( number_arguments > 6 && *method == PolarDistortion )
01135 || number_arguments > 8 ) {
01136 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01137 "InvalidArgument", "%s : number of arguments",
01138 *method == PolarDistortion ? "Polar" : "DePolar");
01139 coeff=(double *) RelinquishMagickMemory(coeff);
01140 return((double *) NULL);
01141 }
01142
01143 if ( number_arguments >= 1 )
01144 coeff[0] = arguments[0];
01145 else
01146 coeff[0] = 0.0;
01147
01148 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
01149
01150 if ( number_arguments >= 4 ) {
01151 coeff[2] = arguments[2];
01152 coeff[3] = arguments[3];
01153 }
01154 else {
01155 coeff[2] = (double)(image->columns)/2.0+image->page.x;
01156 coeff[3] = (double)(image->rows)/2.0+image->page.y;
01157 }
01158
01159 coeff[4] = -MagickPI;
01160 if ( number_arguments >= 5 )
01161 coeff[4] = DegreesToRadians(arguments[4]);
01162 coeff[5] = coeff[4];
01163 if ( number_arguments >= 6 )
01164 coeff[5] = DegreesToRadians(arguments[5]);
01165 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
01166 coeff[5] += Magick2PI;
01167
01168 if ( coeff[0] < MagickEpsilon ) {
01169
01170 if ( fabs(coeff[0]) < MagickEpsilon ) {
01171 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
01172 fabs(coeff[3]-image->page.y));
01173 coeff[0]=MagickMin(coeff[0],
01174 fabs(coeff[2]-image->page.x-image->columns));
01175 coeff[0]=MagickMin(coeff[0],
01176 fabs(coeff[3]-image->page.y-image->rows));
01177 }
01178
01179 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
01180 double rx,ry;
01181 rx = coeff[2]-image->page.x;
01182 ry = coeff[3]-image->page.y;
01183 coeff[0] = rx*rx+ry*ry;
01184 ry = coeff[3]-image->page.y-image->rows;
01185 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01186 rx = coeff[2]-image->page.x-image->columns;
01187 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01188 ry = coeff[3]-image->page.y;
01189 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01190 coeff[0] = sqrt(coeff[0]);
01191 }
01192 }
01193
01194 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
01195 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
01196 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01197 "InvalidArgument", "%s : Invalid Radius",
01198 *method == PolarDistortion ? "Polar" : "DePolar");
01199 coeff=(double *) RelinquishMagickMemory(coeff);
01200 return((double *) NULL);
01201 }
01202
01203 if ( *method == PolarDistortion ) {
01204 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
01205 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
01206 }
01207 else {
01208 coeff[6]=(coeff[5]-coeff[4])/image->columns;
01209 coeff[7]=(coeff[0]-coeff[1])/image->rows;
01210 }
01211 return(coeff);
01212 }
01213 case BarrelDistortion:
01214 case BarrelInverseDistortion:
01215 {
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234 double
01235 rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
01236
01237 if ( number_arguments != 4 && number_arguments != 6 &&
01238 number_arguments != 8 && number_arguments != 10 ) {
01239 coeff=(double *) RelinquishMagickMemory(coeff);
01240 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01241 "InvalidArgument", "%s : '%s'", "Barrel(Inv)",
01242 "number of arguments" );
01243 return((double *) NULL);
01244 }
01245
01246 coeff[0] = arguments[0];
01247 coeff[1] = arguments[1];
01248 coeff[2] = arguments[2];
01249 if ( number_arguments == 3 || number_arguments == 5 )
01250 coeff[3] = 1 - arguments[0] - arguments[1] - arguments[2];
01251 else
01252 coeff[3] = arguments[3];
01253
01254 coeff[0] *= pow(rscale,3.0);
01255 coeff[1] *= rscale*rscale;
01256 coeff[2] *= rscale;
01257
01258 if ( number_arguments >= 8 ) {
01259 coeff[4] = arguments[4] * pow(rscale,3.0);
01260 coeff[5] = arguments[5] * rscale*rscale;
01261 coeff[6] = arguments[6] * rscale;
01262 coeff[7] = arguments[7];
01263 }
01264 else {
01265 coeff[4] = coeff[0];
01266 coeff[5] = coeff[1];
01267 coeff[6] = coeff[2];
01268 coeff[7] = coeff[3];
01269 }
01270
01271 coeff[8] = ((double)image->columns-1)/2.0 + image->page.x;
01272 coeff[9] = ((double)image->rows-1)/2.0 + image->page.y;
01273 if ( number_arguments == 5 ) {
01274 coeff[8] = arguments[3];
01275 coeff[9] = arguments[4];
01276 }
01277 if ( number_arguments == 6 ) {
01278 coeff[8] = arguments[4];
01279 coeff[9] = arguments[5];
01280 }
01281 if ( number_arguments == 10 ) {
01282 coeff[8] = arguments[8];
01283 coeff[9] = arguments[9];
01284 }
01285 return(coeff);
01286 }
01287 case ShepardsDistortion:
01288 case VoronoiColorInterpolate:
01289 {
01290
01291
01292
01293
01294
01295 if ( number_arguments%cp_size != 0 ||
01296 number_arguments < cp_size ) {
01297 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01298 "InvalidArgument", "%s : 'require at least %ld CPs'",
01299 "Shepards", 1UL);
01300 coeff=(double *) RelinquishMagickMemory(coeff);
01301 return((double *) NULL);
01302 }
01303 return(coeff);
01304 }
01305 default:
01306 break;
01307 }
01308
01309 assert(! "No Method Handler");
01310 return((double *) NULL);
01311 }
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401 MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
01402 const unsigned long number_arguments,const double *arguments,
01403 MagickBooleanType bestfit,ExceptionInfo *exception)
01404 {
01405 #define DistortImageTag "Distort/Image"
01406
01407 double
01408 *coeff,
01409 output_scaling;
01410
01411 Image
01412 *distort_image;
01413
01414 RectangleInfo
01415 geometry;
01416
01417 MagickBooleanType
01418 viewport_given;
01419
01420 assert(image != (Image *) NULL);
01421 assert(image->signature == MagickSignature);
01422 if (image->debug != MagickFalse)
01423 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01424 assert(exception != (ExceptionInfo *) NULL);
01425 assert(exception->signature == MagickSignature);
01426
01427
01428
01429
01430
01431
01432
01433
01434 coeff = GenerateCoefficients(image, &method, number_arguments,
01435 arguments, 0, exception);
01436 if ( coeff == (double *) NULL )
01437 return((Image *) NULL);
01438
01439
01440
01441
01442
01443
01444
01445 geometry.width=image->columns;
01446 geometry.height=image->rows;
01447 geometry.x=0;
01448 geometry.y=0;
01449
01450 if ( method == ArcDistortion ) {
01451
01452 bestfit = MagickTrue;
01453 }
01454
01455
01456 if ( bestfit ) {
01457 PointInfo
01458 s,d,min,max;
01459
01460 s.x=s.y=min.x=max.x=min.y=max.y=0.0;
01461
01462
01463 #define InitalBounds(p) \
01464 { \
01465 \
01466 min.x = max.x = p.x; \
01467 min.y = max.y = p.y; \
01468 }
01469 #define ExpandBounds(p) \
01470 { \
01471 \
01472 min.x = MagickMin(min.x,p.x); \
01473 max.x = MagickMax(max.x,p.x); \
01474 min.y = MagickMin(min.y,p.y); \
01475 max.y = MagickMax(max.y,p.y); \
01476 }
01477
01478 switch (method)
01479 {
01480 case AffineDistortion:
01481 { double inverse[6];
01482 InvertAffineCoefficients(coeff, inverse);
01483 s.x = (double) image->page.x;
01484 s.y = (double) image->page.y;
01485 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01486 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01487 InitalBounds(d);
01488 s.x = (double) image->page.x+image->columns;
01489 s.y = (double) image->page.y;
01490 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01491 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01492 ExpandBounds(d);
01493 s.x = (double) image->page.x;
01494 s.y = (double) image->page.y+image->rows;
01495 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01496 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01497 ExpandBounds(d);
01498 s.x = (double) image->page.x+image->columns;
01499 s.y = (double) image->page.y+image->rows;
01500 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01501 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01502 ExpandBounds(d);
01503 break;
01504 }
01505 case PerspectiveDistortion:
01506 { double inverse[8], scale;
01507 InvertPerspectiveCoefficients(coeff, inverse);
01508 s.x = (double) image->page.x;
01509 s.y = (double) image->page.y;
01510 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01511 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01512 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01513 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01514 InitalBounds(d);
01515 s.x = (double) image->page.x+image->columns;
01516 s.y = (double) image->page.y;
01517 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01518 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01519 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01520 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01521 ExpandBounds(d);
01522 s.x = (double) image->page.x;
01523 s.y = (double) image->page.y+image->rows;
01524 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01525 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01526 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01527 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01528 ExpandBounds(d);
01529 s.x = (double) image->page.x+image->columns;
01530 s.y = (double) image->page.y+image->rows;
01531 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01532 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01533 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01534 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01535 ExpandBounds(d);
01536 break;
01537 }
01538 case ArcDistortion:
01539 { double a, ca, sa;
01540
01541 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
01542 d.x = coeff[2]*ca;
01543 d.y = coeff[2]*sa;
01544 InitalBounds(d);
01545 d.x = (coeff[2]-coeff[3])*ca;
01546 d.y = (coeff[2]-coeff[3])*sa;
01547 ExpandBounds(d);
01548 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
01549 d.x = coeff[2]*ca;
01550 d.y = coeff[2]*sa;
01551 ExpandBounds(d);
01552 d.x = (coeff[2]-coeff[3])*ca;
01553 d.y = (coeff[2]-coeff[3])*sa;
01554 ExpandBounds(d);
01555
01556 for( a=ceil((coeff[0]-coeff[1]/2.0)/MagickPI2)*MagickPI2;
01557 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
01558 ca = cos(a); sa = sin(a);
01559 d.x = coeff[2]*ca;
01560 d.y = coeff[2]*sa;
01561 ExpandBounds(d);
01562 }
01563
01564
01565
01566
01567
01568 coeff[1] = Magick2PI*image->columns/coeff[1];
01569 coeff[3] = (double)image->rows/coeff[3];
01570 break;
01571 }
01572 case PolarDistortion:
01573 {
01574 if (number_arguments < 2)
01575 coeff[2] = coeff[3] = 0.0;
01576 min.x = coeff[2]-coeff[0];
01577 max.x = coeff[2]+coeff[0];
01578 min.y = coeff[3]-coeff[0];
01579 max.y = coeff[3]+coeff[0];
01580
01581 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
01582 break;
01583 }
01584 case DePolarDistortion:
01585 {
01586
01587
01588 geometry.x = geometry.y = 0;
01589 geometry.height = (unsigned long) ceil(coeff[0]-coeff[1]);
01590 geometry.width = (unsigned long)
01591 ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
01592 break;
01593 }
01594 case ShepardsDistortion:
01595 case BilinearForwardDistortion:
01596 case BilinearReverseDistortion:
01597 #if 0
01598 case QuadrilateralDistortion:
01599 #endif
01600 case PolynomialDistortion:
01601 case BarrelDistortion:
01602 case BarrelInverseDistortion:
01603 default:
01604
01605 bestfit = MagickFalse;
01606 break;
01607 }
01608
01609
01610
01611 if ( bestfit && method != DePolarDistortion ) {
01612 geometry.x = (long) ceil(min.x-0.5);
01613 geometry.y = (long) ceil(min.y-0.5);
01614 geometry.width=(unsigned long) floor(max.x-geometry.x+0.5);
01615 geometry.height=(unsigned long) floor(max.y-geometry.y+0.5);
01616 }
01617
01618 if ( method == DePolarDistortion ) {
01619 coeff[6]=(coeff[5]-coeff[4])/geometry.width;
01620 coeff[7]=(coeff[0]-coeff[1])/geometry.height;
01621 }
01622 }
01623
01624
01625
01626
01627
01628 { const char *artifact=GetImageArtifact(image,"distort:viewport");
01629 viewport_given = MagickFalse;
01630 if ( artifact != (const char *) NULL ) {
01631 (void) ParseAbsoluteGeometry(artifact,&geometry);
01632 viewport_given = MagickTrue;
01633 }
01634 }
01635
01636
01637 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
01638 register long
01639 i;
01640 char image_gen[MaxTextExtent];
01641 const char *lookup;
01642
01643
01644 if ( bestfit || viewport_given ) {
01645 (void) FormatMagickString(image_gen, MaxTextExtent," -size %lux%lu "
01646 "-page %+ld%+ld xc: +insert \\\n",geometry.width,geometry.height,
01647 geometry.x,geometry.y);
01648 lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
01649 }
01650 else {
01651 image_gen[0] = '\0';
01652 lookup = "p{ xx-page.x-.5, yy-page.x-.5 }";
01653 }
01654
01655 switch (method) {
01656 case AffineDistortion:
01657 {
01658 double *inverse;
01659
01660 inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
01661 if (inverse == (double *) NULL) {
01662 coeff = (double *) RelinquishMagickMemory(coeff);
01663 (void) ThrowMagickException(exception,GetMagickModule(),
01664 ResourceLimitError,"MemoryAllocationFailed",
01665 "%s", "DistortImages");
01666 return((Image *) NULL);
01667 }
01668 InvertAffineCoefficients(coeff, inverse);
01669 CoefficientsToAffineArgs(inverse);
01670 fprintf(stderr, "Affine Projection:\n");
01671 fprintf(stderr, " -distort AffineProjection \\\n '");
01672 for (i=0; i<5; i++)
01673 fprintf(stderr, "%lf,", inverse[i]);
01674 fprintf(stderr, "%lf'\n", inverse[5]);
01675 inverse = (double *) RelinquishMagickMemory(inverse);
01676
01677 fprintf(stderr, "Affine Distort, FX Equivelent:\n");
01678 fprintf(stderr, "%s", image_gen);
01679 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01680 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
01681 coeff[0], coeff[1], coeff[2]);
01682 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
01683 coeff[3], coeff[4], coeff[5]);
01684 fprintf(stderr, " %s'\n", lookup);
01685
01686 break;
01687 }
01688
01689 case PerspectiveDistortion:
01690 {
01691 double *inverse;
01692
01693 inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
01694 if (inverse == (double *) NULL) {
01695 coeff = (double *) RelinquishMagickMemory(coeff);
01696 (void) ThrowMagickException(exception,GetMagickModule(),
01697 ResourceLimitError,"MemoryAllocationFailed",
01698 "%s", "DistortCoefficients");
01699 return((Image *) NULL);
01700 }
01701 InvertPerspectiveCoefficients(coeff, inverse);
01702 fprintf(stderr, "Perspective Projection:\n");
01703 fprintf(stderr, " -distort PerspectiveProjection \\\n '");
01704 for (i=0; i<4; i++)
01705 fprintf(stderr, "%lf, ", inverse[i]);
01706 fprintf(stderr, "\n ");
01707 for (; i<7; i++)
01708 fprintf(stderr, "%lf, ", inverse[i]);
01709 fprintf(stderr, "%lf'\n", inverse[7]);
01710 inverse = (double *) RelinquishMagickMemory(inverse);
01711
01712 fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
01713 fprintf(stderr, "%s", image_gen);
01714 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01715 fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
01716 coeff[6], coeff[7]);
01717 fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
01718 coeff[0], coeff[1], coeff[2]);
01719 fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
01720 coeff[3], coeff[4], coeff[5]);
01721 fprintf(stderr, " rr%s0 ? %s : blue'\n",
01722 coeff[8] < 0 ? "<" : ">", lookup);
01723 break;
01724 }
01725
01726 case BilinearForwardDistortion:
01727 fprintf(stderr, "BilinearForward Mapping Equations:\n");
01728 fprintf(stderr, "%s", image_gen);
01729 fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
01730 coeff[0], coeff[1], coeff[2], coeff[3]);
01731 fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
01732 coeff[4], coeff[5], coeff[6], coeff[7]);
01733 #if 0
01734
01735 fprintf(stderr, " c8 = %+lf c9 = 2*a = %+lf;\n",
01736 coeff[8], coeff[9]);
01737 #endif
01738 fprintf(stderr, "BilinearForward Distort, FX Equivelent:\n");
01739 fprintf(stderr, "%s", image_gen);
01740 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
01741 0.5-coeff[3], 0.5-coeff[7]);
01742 fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
01743 coeff[6], -coeff[2], coeff[8]);
01744
01745 if ( coeff[9] != 0 ) {
01746 fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
01747 -2*coeff[9], coeff[4], -coeff[0]);
01748 fprintf(stderr, " yy=( -bb + sqrt(rt) ) / %lf;\n",
01749 coeff[9]);
01750 } else
01751 fprintf(stderr, " yy=(%lf*ii%+lf*jj)/bb;\n",
01752 -coeff[4], coeff[0]);
01753 fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
01754 -coeff[1], coeff[0], coeff[2]);
01755 if ( coeff[9] != 0 )
01756 fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
01757 else
01758 fprintf(stderr, " %s'\n", lookup);
01759 break;
01760
01761 case BilinearReverseDistortion:
01762 #if 0
01763 fprintf(stderr, "Polynomial Projection Distort:\n");
01764 fprintf(stderr, " -distort PolynomialProjection \\\n");
01765 fprintf(stderr, " '1.5, %lf, %lf, %lf, %lf,\n",
01766 coeff[3], coeff[0], coeff[1], coeff[2]);
01767 fprintf(stderr, " %lf, %lf, %lf, %lf'\n",
01768 coeff[7], coeff[4], coeff[5], coeff[6]);
01769 #endif
01770 fprintf(stderr, "BilinearReverse Distort, FX Equivelent:\n");
01771 fprintf(stderr, "%s", image_gen);
01772 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01773 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
01774 coeff[0], coeff[1], coeff[2], coeff[3]);
01775 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
01776 coeff[4], coeff[5], coeff[6], coeff[7]);
01777 fprintf(stderr, " %s'\n", lookup);
01778 break;
01779
01780 case PolynomialDistortion:
01781 {
01782 unsigned long nterms = (unsigned long) coeff[1];
01783 fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
01784 coeff[0], nterms);
01785 fprintf(stderr, "%s", image_gen);
01786 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01787 fprintf(stderr, " xx =");
01788 for (i=0; i<(long) nterms; i++) {
01789 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
01790 fprintf(stderr, " %+lf%s", coeff[2+i],
01791 poly_basis_str(i));
01792 }
01793 fprintf(stderr, ";\n yy =");
01794 for (i=0; i<(long) nterms; i++) {
01795 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
01796 fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
01797 poly_basis_str(i));
01798 }
01799 fprintf(stderr, ";\n %s'\n", lookup);
01800 break;
01801 }
01802 case ArcDistortion:
01803 {
01804 fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
01805 for ( i=0; i<5; i++ )
01806 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01807 fprintf(stderr, "Arc Distort, FX Equivelent:\n");
01808 fprintf(stderr, "%s", image_gen);
01809 fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
01810 fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
01811 -coeff[0]);
01812 fprintf(stderr, " xx=xx-round(xx);\n");
01813 fprintf(stderr, " xx=xx*%lf %+lf;\n",
01814 coeff[1], coeff[4]);
01815 fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
01816 coeff[2], coeff[3]);
01817 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01818 break;
01819 }
01820 case PolarDistortion:
01821 {
01822 fprintf(stderr, "Polar Distort, Internal Coefficents\n");
01823 for ( i=0; i<8; i++ )
01824 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01825 fprintf(stderr, "Polar Distort, FX Equivelent:\n");
01826 fprintf(stderr, "%s", image_gen);
01827 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
01828 -coeff[2], -coeff[3]);
01829 fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
01830 -(coeff[4]+coeff[5])/2 );
01831 fprintf(stderr, " xx=xx-round(xx);\n");
01832 fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
01833 coeff[6] );
01834 fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
01835 -coeff[1], coeff[7] );
01836 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01837 break;
01838 }
01839 case DePolarDistortion:
01840 {
01841 fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
01842 for ( i=0; i<8; i++ )
01843 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01844 fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
01845 fprintf(stderr, "%s", image_gen);
01846 fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
01847 fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
01848 fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
01849 fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
01850 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01851 break;
01852 }
01853 case BarrelDistortion:
01854 case BarrelInverseDistortion:
01855 { double xc,yc;
01856 xc = ((double)image->columns-1.0)/2.0 + image->page.x;
01857 yc = ((double)image->rows-1.0)/2.0 + image->page.y;
01858 fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
01859 method == BarrelDistortion ? "" : "Inv");
01860 fprintf(stderr, "%s", image_gen);
01861 if ( fabs(coeff[8]-xc) < 0.1 && fabs(coeff[9]-yc) < 0.1 )
01862 fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
01863 else
01864 fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
01865 coeff[8], coeff[9]);
01866 fprintf(stderr,
01867 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
01868 fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
01869 method == BarrelDistortion ? "*" : "/",
01870 coeff[0],coeff[1],coeff[2],coeff[3]);
01871 fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
01872 method == BarrelDistortion ? "*" : "/",
01873 coeff[4],coeff[5],coeff[6],coeff[7]);
01874 fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
01875 }
01876 default:
01877 break;
01878 }
01879 }
01880
01881
01882
01883
01884
01885
01886 { const char *artifact;
01887 artifact=GetImageArtifact(image,"distort:scale");
01888 output_scaling = 1.0;
01889 if (artifact != (const char *) NULL) {
01890 output_scaling = fabs(StringToDouble(artifact));
01891 geometry.width *= output_scaling;
01892 geometry.height *= output_scaling;
01893 geometry.x *= output_scaling;
01894 geometry.y *= output_scaling;
01895 if ( output_scaling < 0.1 ) {
01896 coeff = (double *) RelinquishMagickMemory(coeff);
01897 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01898 "InvalidArgument","%s", "-set option:distort:scale" );
01899 return((Image *) NULL);
01900 }
01901 output_scaling = 1/output_scaling;
01902 }
01903 }
01904 #define ScaleFilter(F,A,B,C,D) \
01905 ScaleResampleFilter( (F), \
01906 output_scaling*(A), output_scaling*(B), \
01907 output_scaling*(C), output_scaling*(D) )
01908
01909
01910
01911
01912 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
01913 exception);
01914 if (distort_image == (Image *) NULL)
01915 return((Image *) NULL);
01916 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
01917 {
01918 InheritException(exception,&distort_image->exception);
01919 distort_image=DestroyImage(distort_image);
01920 return((Image *) NULL);
01921 }
01922 distort_image->page.x=geometry.x;
01923 distort_image->page.y=geometry.y;
01924 if (distort_image->background_color.opacity != OpaqueOpacity)
01925 distort_image->matte=MagickTrue;
01926
01927 {
01928
01929
01930 long
01931 j,
01932 progress,
01933 status;
01934
01935 MagickPixelPacket
01936 zero;
01937
01938 ResampleFilter
01939 **restrict resample_filter;
01940
01941 CacheView
01942 *distort_view;
01943
01944 status=MagickTrue;
01945 progress=0;
01946 GetMagickPixelPacket(distort_image,&zero);
01947 resample_filter=AcquireResampleFilterThreadSet(image,
01948 UndefinedVirtualPixelMethod,MagickFalse,exception);
01949 distort_view=AcquireCacheView(distort_image);
01950 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01951 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01952 #endif
01953 for (j=0; j < (long) distort_image->rows; j++)
01954 {
01955 double
01956 validity;
01957
01958 MagickBooleanType
01959 sync;
01960
01961 MagickPixelPacket
01962 pixel,
01963 invalid;
01964
01965 PointInfo
01966 d,
01967 s;
01968
01969 register IndexPacket
01970 *restrict indexes;
01971
01972 register long
01973 i,
01974 id;
01975
01976 register PixelPacket
01977 *restrict q;
01978
01979 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
01980 exception);
01981 if (q == (PixelPacket *) NULL)
01982 {
01983 status=MagickFalse;
01984 continue;
01985 }
01986 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
01987 pixel=zero;
01988
01989
01990
01991
01992 id=GetOpenMPThreadId();
01993 switch (method)
01994 {
01995 case AffineDistortion:
01996 ScaleFilter( resample_filter[id],
01997 coeff[0], coeff[1],
01998 coeff[3], coeff[4] );
01999 break;
02000 default:
02001 break;
02002 }
02003
02004
02005
02006
02007
02008
02009 validity = 1.0;
02010
02011 GetMagickPixelPacket(distort_image,&invalid);
02012 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
02013 (IndexPacket *) NULL, &invalid);
02014 if (distort_image->colorspace == CMYKColorspace)
02015 ConvertRGBToCMYK(&invalid);
02016
02017 for (i=0; i < (long) distort_image->columns; i++)
02018 {
02019
02020 d.x = (double) (geometry.x+i+0.5)*output_scaling;
02021 d.y = (double) (geometry.y+j+0.5)*output_scaling;
02022 s = d;
02023 switch (method)
02024 {
02025 case AffineDistortion:
02026 {
02027 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
02028 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
02029
02030 break;
02031 }
02032 case PerspectiveDistortion:
02033 {
02034 double
02035 p,q,r,abs_r,abs_c6,abs_c7,scale;
02036
02037 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
02038 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
02039 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
02040
02041 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
02042
02043 abs_r = fabs(r)*2;
02044 abs_c6 = fabs(coeff[6]);
02045 abs_c7 = fabs(coeff[7]);
02046 if ( abs_c6 > abs_c7 ) {
02047 if ( abs_r < abs_c6 )
02048 validity = 0.5 - coeff[8]*r/coeff[6];
02049 }
02050 else if ( abs_r < abs_c7 )
02051 validity = 0.5 - coeff[8]*r/coeff[7];
02052
02053 if ( validity > 0.0 ) {
02054
02055 scale = 1.0/r;
02056 s.x = p*scale;
02057 s.y = q*scale;
02058
02059 scale *= scale;
02060 ScaleFilter( resample_filter[id],
02061 (r*coeff[0] - p*coeff[6])*scale,
02062 (r*coeff[1] - p*coeff[7])*scale,
02063 (r*coeff[3] - q*coeff[6])*scale,
02064 (r*coeff[4] - q*coeff[7])*scale );
02065 }
02066 break;
02067 }
02068 case BilinearReverseDistortion:
02069 {
02070
02071 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
02072 s.y=coeff[4]*d.x+coeff[5]*d.y
02073 +coeff[6]*d.x*d.y+coeff[7];
02074
02075 ScaleFilter( resample_filter[id],
02076 coeff[0] + coeff[2]*d.y,
02077 coeff[1] + coeff[2]*d.x,
02078 coeff[4] + coeff[6]*d.y,
02079 coeff[5] + coeff[6]*d.x );
02080 break;
02081 }
02082 case BilinearForwardDistortion:
02083 {
02084
02085
02086 double b,c;
02087 d.x -= coeff[3]; d.y -= coeff[7];
02088 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
02089 c = coeff[4]*d.x - coeff[0]*d.y;
02090
02091 validity = 1.0;
02092
02093 if ( fabs(coeff[9]) < MagickEpsilon )
02094 s.y = -c/b;
02095 else {
02096 c = b*b - 2*coeff[9]*c;
02097 if ( c < 0.0 )
02098 validity = 0.0;
02099 else
02100 s.y = ( -b + sqrt(c) )/coeff[9];
02101 }
02102 if ( validity > 0.0 )
02103 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
02104
02105
02106
02107
02108
02109
02110 break;
02111 }
02112 #if 0
02113 case QuadrilateralDistortion:
02114
02115
02116 break;
02117 #endif
02118 case PolynomialDistortion:
02119 {
02120
02121 register long
02122 k;
02123 long
02124 nterms=(long)coeff[1];
02125
02126 PointInfo
02127 du,dv;
02128
02129 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
02130 for(k=0; k < nterms; k++) {
02131 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
02132 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
02133 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
02134 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
02135 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
02136 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
02137 }
02138 ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
02139 break;
02140 }
02141 case ArcDistortion:
02142 {
02143
02144 s.x = (atan2(d.y,d.x) - coeff[0])/Magick2PI;
02145 s.x -= MagickRound(s.x);
02146 s.y = hypot(d.x,d.y);
02147
02148
02149
02150
02151
02152
02153 if ( s.y > MagickEpsilon )
02154 ScaleFilter( resample_filter[id],
02155 coeff[1]/(Magick2PI*s.y), 0, 0, coeff[3] );
02156 else
02157 ScaleFilter( resample_filter[id],
02158 distort_image->columns*2, 0, 0, coeff[3] );
02159
02160
02161 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
02162 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
02163 break;
02164 }
02165 case PolarDistortion:
02166 {
02167 d.x -= coeff[2];
02168 d.y -= coeff[3];
02169 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
02170 s.x /= Magick2PI;
02171 s.x -= MagickRound(s.x);
02172 s.x *= Magick2PI;
02173 s.y = hypot(d.x,d.y);
02174
02175
02176
02177
02178 if ( s.y > MagickEpsilon )
02179 ScaleFilter( resample_filter[id],
02180 coeff[6]/(Magick2PI*s.y), 0, 0, coeff[7] );
02181 else
02182 ScaleFilter( resample_filter[id],
02183 distort_image->columns*2, 0, 0, coeff[7] );
02184
02185
02186 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
02187 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
02188 break;
02189 }
02190 case DePolarDistortion:
02191 {
02192
02193 d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
02194 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
02195 s.x = d.y*sin(d.x) + coeff[2];
02196 s.y = d.y*cos(d.x) + coeff[3];
02197
02198 break;
02199 }
02200 case BarrelDistortion:
02201 case BarrelInverseDistortion:
02202 {
02203 double r,fx,fy,gx,gy;
02204
02205 d.x -= coeff[8];
02206 d.y -= coeff[9];
02207 r = sqrt(d.x*d.x+d.y*d.y);
02208 if ( r > MagickEpsilon ) {
02209 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
02210 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
02211 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
02212 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
02213
02214 if ( method == BarrelInverseDistortion ) {
02215 fx = 1/fx; fy = 1/fy;
02216 gx *= -fx*fx; gy *= -fy*fy;
02217 }
02218
02219 s.x = d.x*fx + coeff[8];
02220 s.y = d.y*fy + coeff[9];
02221 ScaleFilter( resample_filter[id],
02222 gx*d.x*d.x + fx, gx*d.x*d.y,
02223 gy*d.x*d.y, gy*d.y*d.y + fy );
02224 }
02225 else {
02226 s.x=s.y=0.0;
02227 if ( method == BarrelDistortion )
02228 ScaleFilter( resample_filter[id],
02229 coeff[3], 0, 0, coeff[7] );
02230 else
02231
02232 ScaleFilter( resample_filter[id],
02233 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
02234 }
02235 break;
02236 }
02237 case ShepardsDistortion:
02238 {
02239
02240
02241
02242
02243
02244 unsigned long
02245 i;
02246 double
02247 denominator;
02248
02249 denominator = s.x = s.y = 0;
02250 for(i=0; i<number_arguments; i+=4) {
02251 double weight =
02252 ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
02253 + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
02254 if ( weight != 0 )
02255 weight = 1/weight;
02256 else
02257 weight = 1;
02258
02259 s.x += (arguments[ i ]-arguments[i+2])*weight;
02260 s.y += (arguments[i+1]-arguments[i+3])*weight;
02261 denominator += weight;
02262 }
02263 s.x /= denominator;
02264 s.y /= denominator;
02265 s.x += d.x;
02266 s.y += d.y;
02267
02268
02269
02270 break;
02271 }
02272 default:
02273 break;
02274 }
02275
02276 if ( bestfit && method != ArcDistortion ) {
02277 s.x -= image->page.x;
02278 s.y -= image->page.y;
02279 }
02280 s.x -= 0.5;
02281 s.y -= 0.5;
02282
02283 if ( validity <= 0.0 ) {
02284
02285 SetPixelPacket(distort_image,&invalid,q,indexes);
02286 }
02287 else {
02288
02289 (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
02290
02291 if ( validity < 1.0 ) {
02292
02293
02294 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
02295 &pixel);
02296 }
02297 SetPixelPacket(distort_image,&pixel,q,indexes);
02298 }
02299 q++;
02300 indexes++;
02301 }
02302 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
02303 if (sync == MagickFalse)
02304 status=MagickFalse;
02305 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02306 {
02307 MagickBooleanType
02308 proceed;
02309
02310 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02311 #pragma omp critical (MagickCore_DistortImage)
02312 #endif
02313 proceed=SetImageProgress(image,DistortImageTag,progress++,
02314 image->rows);
02315 if (proceed == MagickFalse)
02316 status=MagickFalse;
02317 }
02318 #if 0
02319 fprintf(stderr, "\n");
02320 #endif
02321 }
02322 distort_view=DestroyCacheView(distort_view);
02323 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
02324
02325 if (status == MagickFalse)
02326 distort_image=DestroyImage(distort_image);
02327 }
02328
02329
02330
02331
02332 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
02333 distort_image->page.x = 0;
02334 distort_image->page.y = 0;
02335 }
02336 coeff = (double *) RelinquishMagickMemory(coeff);
02337 return(distort_image);
02338 }
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380
02381
02382 MagickExport Image *SparseColorImage(const Image *image,
02383 const ChannelType channel,const SparseColorMethod method,
02384 const unsigned long number_arguments,const double *arguments,
02385 ExceptionInfo *exception)
02386 {
02387 #define SparseColorTag "Distort/SparseColor"
02388
02389 DistortImageMethod
02390 distort_method;
02391
02392 double
02393 *coeff;
02394
02395 Image
02396 *sparse_image;
02397
02398 MagickPixelPacket
02399 zero;
02400
02401 unsigned long
02402 number_colors;
02403
02404 assert(image != (Image *) NULL);
02405 assert(image->signature == MagickSignature);
02406 if (image->debug != MagickFalse)
02407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02408 assert(exception != (ExceptionInfo *) NULL);
02409 assert(exception->signature == MagickSignature);
02410
02411
02412 number_colors=0;
02413 if ( channel & RedChannel ) number_colors++;
02414 if ( channel & GreenChannel ) number_colors++;
02415 if ( channel & BlueChannel ) number_colors++;
02416 if ( channel & IndexChannel ) number_colors++;
02417 if ( channel & OpacityChannel ) number_colors++;
02418
02419
02420
02421
02422
02423 distort_method=(DistortImageMethod) method;
02424 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
02425 arguments, number_colors, exception);
02426 if ( coeff == (double *) NULL )
02427 return((Image *) NULL);
02428
02429
02430 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
02431
02432 switch (method) {
02433 case BarycentricColorInterpolate:
02434 {
02435 register long x=0;
02436 fprintf(stderr, "Barycentric Sparse Color:\n");
02437 if ( channel & RedChannel )
02438 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
02439 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02440 if ( channel & GreenChannel )
02441 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
02442 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02443 if ( channel & BlueChannel )
02444 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
02445 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02446 if ( channel & IndexChannel )
02447 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
02448 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02449 if ( channel & OpacityChannel )
02450 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
02451 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02452 break;
02453 }
02454 case BilinearColorInterpolate:
02455 {
02456 register long x=0;
02457 fprintf(stderr, "Bilinear Sparse Color\n");
02458 if ( channel & RedChannel )
02459 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02460 coeff[ x ], coeff[x+1],
02461 coeff[x+2], coeff[x+3]),x+=4;
02462 if ( channel & GreenChannel )
02463 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02464 coeff[ x ], coeff[x+1],
02465 coeff[x+2], coeff[x+3]),x+=4;
02466 if ( channel & BlueChannel )
02467 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02468 coeff[ x ], coeff[x+1],
02469 coeff[x+2], coeff[x+3]),x+=4;
02470 if ( channel & IndexChannel )
02471 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02472 coeff[ x ], coeff[x+1],
02473 coeff[x+2], coeff[x+3]),x+=4;
02474 if ( channel & OpacityChannel )
02475 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02476 coeff[ x ], coeff[x+1],
02477 coeff[x+2], coeff[x+3]),x+=4;
02478 break;
02479 }
02480 default:
02481
02482 break;
02483 }
02484 }
02485
02486
02487
02488
02489
02490
02491
02492 sparse_image=CloneImage(image,image->columns,image->rows,MagickTrue,
02493 exception);
02494 if (sparse_image == (Image *) NULL)
02495 return((Image *) NULL);
02496 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
02497 {
02498 InheritException(exception,&image->exception);
02499 sparse_image=DestroyImage(sparse_image);
02500 return((Image *) NULL);
02501 }
02502 {
02503 long
02504 j,
02505 progress;
02506
02507 MagickBooleanType
02508 status;
02509
02510 CacheView
02511 *sparse_view;
02512
02513 status=MagickTrue;
02514 progress=0;
02515 GetMagickPixelPacket(sparse_image,&zero);
02516 sparse_view=AcquireCacheView(sparse_image);
02517 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02518 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02519 #endif
02520 for (j=0; j < (long) sparse_image->rows; j++)
02521 {
02522 MagickBooleanType
02523 sync;
02524
02525 MagickPixelPacket
02526 pixel;
02527
02528 register IndexPacket
02529 *restrict indexes;
02530
02531 register long
02532 i;
02533
02534 register PixelPacket
02535 *restrict q;
02536
02537 q=QueueCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
02538 1,exception);
02539 if (q == (PixelPacket *) NULL)
02540 {
02541 status=MagickFalse;
02542 continue;
02543 }
02544
02545 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
02546 pixel=zero;
02547 for (i=0; i < (long) sparse_image->columns; i++)
02548 {
02549 switch (method)
02550 {
02551 case BarycentricColorInterpolate:
02552 {
02553 register long x=0;
02554 if ( channel & RedChannel )
02555 pixel.red = coeff[x]*i +coeff[x+1]*j
02556 +coeff[x+2], x+=3;
02557 if ( channel & GreenChannel )
02558 pixel.green = coeff[x]*i +coeff[x+1]*j
02559 +coeff[x+2], x+=3;
02560 if ( channel & BlueChannel )
02561 pixel.blue = coeff[x]*i +coeff[x+1]*j
02562 +coeff[x+2], x+=3;
02563 if ( channel & IndexChannel )
02564 pixel.index = coeff[x]*i +coeff[x+1]*j
02565 +coeff[x+2], x+=3;
02566 if ( channel & OpacityChannel )
02567 pixel.opacity = coeff[x]*i +coeff[x+1]*j
02568 +coeff[x+2], x+=3;
02569 break;
02570 }
02571 case BilinearColorInterpolate:
02572 {
02573 register long x=0;
02574 if ( channel & RedChannel )
02575 pixel.red = coeff[x]*i + coeff[x+1]*j +
02576 coeff[x+2]*i*j + coeff[x+3], x+=4;
02577 if ( channel & GreenChannel )
02578 pixel.green = coeff[x]*i + coeff[x+1]*j +
02579 coeff[x+2]*i*j + coeff[x+3], x+=4;
02580 if ( channel & BlueChannel )
02581 pixel.blue = coeff[x]*i + coeff[x+1]*j +
02582 coeff[x+2]*i*j + coeff[x+3], x+=4;
02583 if ( channel & IndexChannel )
02584 pixel.index = coeff[x]*i + coeff[x+1]*j +
02585 coeff[x+2]*i*j + coeff[x+3], x+=4;
02586 if ( channel & OpacityChannel )
02587 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
02588 coeff[x+2]*i*j + coeff[x+3], x+=4;
02589 break;
02590 }
02591 case ShepardsColorInterpolate:
02592 {
02593
02594 unsigned long
02595 k;
02596 double
02597 denominator;
02598
02599 if ( channel & RedChannel ) pixel.red = 0.0;
02600 if ( channel & GreenChannel ) pixel.green = 0.0;
02601 if ( channel & BlueChannel ) pixel.blue = 0.0;
02602 if ( channel & IndexChannel ) pixel.index = 0.0;
02603 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
02604 denominator = 0.0;
02605 for(k=0; k<number_arguments; k+=2+number_colors) {
02606 register long x=(long) k+2;
02607 double weight =
02608 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
02609 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
02610 if ( weight != 0 )
02611 weight = 1/weight;
02612 else
02613 weight = 1;
02614 if ( channel & RedChannel )
02615 pixel.red += arguments[x++]*weight;
02616 if ( channel & GreenChannel )
02617 pixel.green += arguments[x++]*weight;
02618 if ( channel & BlueChannel )
02619 pixel.blue += arguments[x++]*weight;
02620 if ( channel & IndexChannel )
02621 pixel.index += arguments[x++]*weight;
02622 if ( channel & OpacityChannel )
02623 pixel.opacity += arguments[x++]*weight;
02624 denominator += weight;
02625 }
02626 if ( channel & RedChannel ) pixel.red /= denominator;
02627 if ( channel & GreenChannel ) pixel.green /= denominator;
02628 if ( channel & BlueChannel ) pixel.blue /= denominator;
02629 if ( channel & IndexChannel ) pixel.index /= denominator;
02630 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
02631 break;
02632 }
02633 case VoronoiColorInterpolate:
02634 default:
02635 {
02636 unsigned long
02637 k;
02638 double
02639 minimum = MagickHuge;
02640
02641 for(k=0; k<number_arguments; k+=2+number_colors) {
02642 double distance =
02643 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
02644 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
02645 if ( distance < minimum ) {
02646 register long x=(long) k+2;
02647 if ( channel & RedChannel ) pixel.red = arguments[x++];
02648 if ( channel & GreenChannel ) pixel.green = arguments[x++];
02649 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
02650 if ( channel & IndexChannel ) pixel.index = arguments[x++];
02651 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
02652 minimum = distance;
02653 }
02654 }
02655 break;
02656 }
02657 }
02658
02659 if ( channel & RedChannel ) pixel.red *= QuantumRange;
02660 if ( channel & GreenChannel ) pixel.green *= QuantumRange;
02661 if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
02662 if ( channel & IndexChannel ) pixel.index *= QuantumRange;
02663 if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
02664 SetPixelPacket(sparse_image,&pixel,q,indexes);
02665 q++;
02666 indexes++;
02667 }
02668 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
02669 if (sync == MagickFalse)
02670 status=MagickFalse;
02671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02672 {
02673 MagickBooleanType
02674 proceed;
02675
02676 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02677 #pragma omp critical (MagickCore_SparseColorImage)
02678 #endif
02679 proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
02680 if (proceed == MagickFalse)
02681 status=MagickFalse;
02682 }
02683 }
02684 sparse_view=DestroyCacheView(sparse_view);
02685 if (status == MagickFalse)
02686 sparse_image=DestroyImage(sparse_image);
02687 }
02688 coeff = (double *) RelinquishMagickMemory(coeff);
02689 return(sparse_image);
02690 }