Home | History | Annotate | Download | only in MagickWand
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %               CCCC   OOO   M   M  PPPP    AAA   RRRR    EEEEE               %
      7 %              C      O   O  MM MM  P   P  A   A  R   R   E                   %
      8 %              C      O   O  M M M  PPPP   AAAAA  RRRR    EEE                 %
      9 %              C      O   O  M   M  P      A   A  R R     E                   %
     10 %               CCCC   OOO   M   M  P      A   A  R  R    EEEEE               %
     11 %                                                                             %
     12 %                                                                             %
     13 %                         Image Comparison Methods                            %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                               December 2003                                 %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %  Use the compare program to mathematically and visually annotate the
     37 %  difference between an image and its reconstruction.
     38 %
     39 */
     40 
     41 /*
     43   Include declarations.
     44 */
     45 #include "MagickWand/studio.h"
     46 #include "MagickWand/MagickWand.h"
     47 #include "MagickWand/mogrify-private.h"
     48 #include "MagickCore/string-private.h"
     49 
     50 /*
     52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     53 %                                                                             %
     54 %                                                                             %
     55 %                                                                             %
     56 %   C o m p a r e I m a g e C o m m a n d                                     %
     57 %                                                                             %
     58 %                                                                             %
     59 %                                                                             %
     60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     61 %
     62 %  CompareImagesCommand() compares two images and returns the difference between
     63 %  them as a distortion metric and as a new image visually annotating their
     64 %  differences.
     65 %
     66 %  The format of the CompareImagesCommand method is:
     67 %
     68 %      MagickBooleanType CompareImagesCommand(ImageInfo *image_info,int argc,
     69 %        char **argv,char **metadata,ExceptionInfo *exception)
     70 %
     71 %  A description of each parameter follows:
     72 %
     73 %    o image_info: the image info.
     74 %
     75 %    o argc: the number of elements in the argument vector.
     76 %
     77 %    o argv: A text array containing the command line arguments.
     78 %
     79 %    o metadata: any metadata is returned here.
     80 %
     81 %    o exception: return any errors or warnings in this structure.
     82 %
     83 */
     84 
     85 static MagickBooleanType CompareUsage(void)
     86 {
     87   const char
     88     **p;
     89 
     90   static const char
     91     *miscellaneous[]=
     92     {
     93       "-channel mask        set the image channel mask",
     94       "-debug events        display copious debugging information",
     95       "-help                print program options",
     96       "-list type           print a list of supported option arguments",
     97       "-log format          format of debugging information",
     98       (char *) NULL
     99     },
    100     *settings[]=
    101     {
    102       "-alpha option        on, activate, off, deactivate, set, opaque, copy",
    103       "                     transparent, extract, background, or shape",
    104       "-authenticate password",
    105       "                     decipher image with this password",
    106       "-colorspace type     alternate image colorspace",
    107       "-compose operator    set image composite operator",
    108       "-compress type       type of pixel compression when writing the image",
    109       "-decipher filename   convert cipher pixels to plain pixels",
    110       "-define format:option",
    111       "                     define one or more image format options",
    112       "-density geometry    horizontal and vertical density of the image",
    113       "-depth value         image depth",
    114       "-dissimilarity-threshold value",
    115       "                     maximum distortion for (sub)image match",
    116       "-encipher filename   convert plain pixels to cipher pixels",
    117       "-extract geometry    extract area from image",
    118       "-format \"string\"     output formatted image characteristics",
    119       "-fuzz distance       colors within this distance are considered equal",
    120       "-highlight-color color",
    121       "                     empasize pixel differences with this color",
    122       "-identify            identify the format and characteristics of the image",
    123       "-interlace type      type of image interlacing scheme",
    124       "-limit type value    pixel cache resource limit",
    125       "-lowlight-color color",
    126       "                     de-emphasize pixel differences with this color",
    127       "-metric type         measure differences between images with this metric",
    128       "-monitor             monitor progress",
    129       "-profile filename    add, delete, or apply an image profile",
    130       "-quality value       JPEG/MIFF/PNG compression level",
    131       "-quiet               suppress all warning messages",
    132       "-quantize colorspace reduce colors in this colorspace",
    133       "-regard-warnings     pay attention to warning messages",
    134       "-respect-parentheses settings remain in effect until parenthesis boundary",
    135       "-sampling-factor geometry",
    136       "                     horizontal and vertical sampling factor",
    137       "-seed value          seed a new sequence of pseudo-random numbers",
    138       "-set attribute value set an image attribute",
    139       "-quality value       JPEG/MIFF/PNG compression level",
    140       "-similarity-threshold value",
    141       "                     minimum distortion for (sub)image match",
    142       "-size geometry       width and height of image",
    143       "-subimage-search     search for subimage",
    144       "-synchronize         synchronize image to storage device",
    145       "-taint               declare the image as modified",
    146       "-transparent-color color",
    147       "                     transparent color",
    148       "-type type           image type",
    149       "-verbose             print detailed information about the image",
    150       "-version             print version information",
    151       "-virtual-pixel method",
    152       "                     virtual pixel access method",
    153       (char *) NULL
    154     };
    155 
    156   ListMagickVersion(stdout);
    157   (void) printf("Usage: %s [options ...] image reconstruct difference\n",
    158     GetClientName());
    159   (void) printf("\nImage Settings:\n");
    160   for (p=settings; *p != (char *) NULL; p++)
    161     (void) printf("  %s\n",*p);
    162   (void) printf("\nMiscellaneous Options:\n");
    163   for (p=miscellaneous; *p != (char *) NULL; p++)
    164     (void) printf("  %s\n",*p);
    165   (void) printf(
    166     "\nBy default, the image format of 'file' is determined by its magic\n");
    167   (void) printf(
    168     "number.  To specify a particular image format, precede the filename\n");
    169   (void) printf(
    170     "with an image format name and a colon (i.e. ps:image) or specify the\n");
    171   (void) printf(
    172     "image type as the filename suffix (i.e. image.ps).  Specify 'file' as\n");
    173   (void) printf("'-' for standard input or output.\n");
    174   return(MagickFalse);
    175 }
    176 
    177 WandExport MagickBooleanType CompareImagesCommand(ImageInfo *image_info,
    178   int argc,char **argv,char **metadata,ExceptionInfo *exception)
    179 {
    180 #define CompareEpsilon  (1.0e-06)
    181 #define DefaultDissimilarityThreshold  0.31830988618379067154
    182 #define DefaultSimilarityThreshold  (-1.0)
    183 #define DestroyCompare() \
    184 { \
    185   if (similarity_image != (Image *) NULL) \
    186     similarity_image=DestroyImageList(similarity_image); \
    187   if (difference_image != (Image *) NULL) \
    188     difference_image=DestroyImageList(difference_image); \
    189   DestroyImageStack(); \
    190   for (i=0; i < (ssize_t) argc; i++) \
    191     argv[i]=DestroyString(argv[i]); \
    192   argv=(char **) RelinquishMagickMemory(argv); \
    193 }
    194 #define ThrowCompareException(asperity,tag,option) \
    195 { \
    196   if (exception->severity < (asperity)) \
    197     (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
    198       "`%s'",option); \
    199   DestroyCompare(); \
    200   return(MagickFalse); \
    201 }
    202 #define ThrowCompareInvalidArgumentException(option,argument) \
    203 { \
    204   (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
    205     "InvalidArgument","'%s': %s",option,argument); \
    206   DestroyCompare(); \
    207   return(MagickFalse); \
    208 }
    209 
    210   char
    211     *filename,
    212     *option;
    213 
    214   const char
    215     *format;
    216 
    217   double
    218     dissimilarity_threshold,
    219     distortion,
    220     similarity_metric,
    221     similarity_threshold;
    222 
    223   Image
    224     *difference_image,
    225     *image,
    226     *reconstruct_image,
    227     *similarity_image;
    228 
    229   ImageStack
    230     image_stack[MaxImageStackDepth+1];
    231 
    232   MagickBooleanType
    233     fire,
    234     pend,
    235     respect_parenthesis,
    236     subimage_search;
    237 
    238   MagickStatusType
    239     status;
    240 
    241   MetricType
    242     metric;
    243 
    244   RectangleInfo
    245     offset;
    246 
    247   register ssize_t
    248     i;
    249 
    250   ssize_t
    251     j,
    252     k;
    253 
    254   /*
    255     Set defaults.
    256   */
    257   assert(image_info != (ImageInfo *) NULL);
    258   assert(image_info->signature == MagickCoreSignature);
    259   if (image_info->debug != MagickFalse)
    260     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
    261   assert(exception != (ExceptionInfo *) NULL);
    262   if (argc == 2)
    263     {
    264       option=argv[1];
    265       if ((LocaleCompare("version",option+1) == 0) ||
    266           (LocaleCompare("-version",option+1) == 0))
    267         {
    268           ListMagickVersion(stdout);
    269           return(MagickTrue);
    270         }
    271     }
    272   if (argc < 3)
    273     return(CompareUsage());
    274   difference_image=NewImageList();
    275   similarity_image=NewImageList();
    276   dissimilarity_threshold=DefaultDissimilarityThreshold;
    277   similarity_threshold=DefaultSimilarityThreshold;
    278   distortion=0.0;
    279   format=(char *) NULL;
    280   j=1;
    281   k=0;
    282   metric=UndefinedErrorMetric;
    283   NewImageStack();
    284   option=(char *) NULL;
    285   pend=MagickFalse;
    286   reconstruct_image=NewImageList();
    287   respect_parenthesis=MagickFalse;
    288   status=MagickTrue;
    289   subimage_search=MagickFalse;
    290   /*
    291     Compare an image.
    292   */
    293   ReadCommandlLine(argc,&argv);
    294   status=ExpandFilenames(&argc,&argv);
    295   if (status == MagickFalse)
    296     ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
    297       GetExceptionMessage(errno));
    298   for (i=1; i < (ssize_t) (argc-1); i++)
    299   {
    300     option=argv[i];
    301     if (LocaleCompare(option,"(") == 0)
    302       {
    303         FireImageStack(MagickTrue,MagickTrue,pend);
    304         if (k == MaxImageStackDepth)
    305           ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
    306             option);
    307         PushImageStack();
    308         continue;
    309       }
    310     if (LocaleCompare(option,")") == 0)
    311       {
    312         FireImageStack(MagickTrue,MagickTrue,MagickTrue);
    313         if (k == 0)
    314           ThrowCompareException(OptionError,"UnableToParseExpression",option);
    315         PopImageStack();
    316         continue;
    317       }
    318     if (IsCommandOption(option) == MagickFalse)
    319       {
    320         Image
    321           *images;
    322 
    323         /*
    324           Read input image.
    325         */
    326         FireImageStack(MagickFalse,MagickFalse,pend);
    327         filename=argv[i];
    328         if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1)))
    329           filename=argv[++i];
    330         images=ReadImages(image_info,filename,exception);
    331         status&=(images != (Image *) NULL) &&
    332           (exception->severity < ErrorException);
    333         if (images == (Image *) NULL)
    334           continue;
    335         AppendImageStack(images);
    336         continue;
    337       }
    338     pend=image != (Image *) NULL ? MagickTrue : MagickFalse;
    339     switch (*(option+1))
    340     {
    341       case 'a':
    342       {
    343         if (LocaleCompare("alpha",option+1) == 0)
    344           {
    345             ssize_t
    346               type;
    347 
    348             if (*option == '+')
    349               break;
    350             i++;
    351             if (i == (ssize_t) argc)
    352               ThrowCompareException(OptionError,"MissingArgument",option);
    353             type=ParseCommandOption(MagickAlphaChannelOptions,MagickFalse,argv[i]);
    354             if (type < 0)
    355               ThrowCompareException(OptionError,"UnrecognizedAlphaChannelOption",
    356                 argv[i]);
    357             break;
    358           }
    359         if (LocaleCompare("authenticate",option+1) == 0)
    360           {
    361             if (*option == '+')
    362               break;
    363             i++;
    364             if (i == (ssize_t) argc)
    365               ThrowCompareException(OptionError,"MissingArgument",option);
    366             break;
    367           }
    368         ThrowCompareException(OptionError,"UnrecognizedOption",option);
    369       }
    370       case 'c':
    371       {
    372         if (LocaleCompare("cache",option+1) == 0)
    373           {
    374             if (*option == '+')
    375               break;
    376             i++;
    377             if (i == (ssize_t) argc)
    378               ThrowCompareException(OptionError,"MissingArgument",option);
    379             if (IsGeometry(argv[i]) == MagickFalse)
    380               ThrowCompareInvalidArgumentException(option,argv[i]);
    381             break;
    382           }
    383         if (LocaleCompare("channel",option+1) == 0)
    384           {
    385             ssize_t
    386               channel;
    387 
    388             if (*option == '+')
    389               break;
    390             i++;
    391             if (i == (ssize_t) argc)
    392               ThrowCompareException(OptionError,"MissingArgument",option);
    393             channel=ParseChannelOption(argv[i]);
    394             if (channel < 0)
    395               ThrowCompareException(OptionError,"UnrecognizedChannelType",
    396                 argv[i]);
    397             (void) SetPixelChannelMask(image,(ChannelType) channel);
    398             break;
    399           }
    400         if (LocaleCompare("colorspace",option+1) == 0)
    401           {
    402             ssize_t
    403               colorspace;
    404 
    405             if (*option == '+')
    406               break;
    407             i++;
    408             if (i == (ssize_t) argc)
    409               ThrowCompareException(OptionError,"MissingArgument",option);
    410             colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
    411               argv[i]);
    412             if (colorspace < 0)
    413               ThrowCompareException(OptionError,"UnrecognizedColorspace",
    414                 argv[i]);
    415             break;
    416           }
    417         if (LocaleCompare("compose",option+1) == 0)
    418           {
    419             ssize_t
    420               compose;
    421 
    422             if (*option == '+')
    423               break;
    424             i++;
    425             if (i == (ssize_t) argc)
    426               ThrowCompareException(OptionError,"MissingArgument",option);
    427             compose=ParseCommandOption(MagickComposeOptions,MagickFalse,
    428               argv[i]);
    429             if (compose < 0)
    430               ThrowCompareException(OptionError,"UnrecognizedComposeOperator",
    431                 argv[i]);
    432             break;
    433           }
    434         if (LocaleCompare("compress",option+1) == 0)
    435           {
    436             ssize_t
    437               compress;
    438 
    439             if (*option == '+')
    440               break;
    441             i++;
    442             if (i == (ssize_t) argc)
    443               ThrowCompareException(OptionError,"MissingArgument",option);
    444             compress=ParseCommandOption(MagickCompressOptions,MagickFalse,
    445               argv[i]);
    446             if (compress < 0)
    447               ThrowCompareException(OptionError,"UnrecognizedImageCompression",
    448                 argv[i]);
    449             break;
    450           }
    451         if (LocaleCompare("concurrent",option+1) == 0)
    452           break;
    453         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    454       }
    455       case 'd':
    456       {
    457         if (LocaleCompare("debug",option+1) == 0)
    458           {
    459             LogEventType
    460               event_mask;
    461 
    462             if (*option == '+')
    463               break;
    464             i++;
    465             if (i == (ssize_t) argc)
    466               ThrowCompareException(OptionError,"MissingArgument",option);
    467             event_mask=SetLogEventMask(argv[i]);
    468             if (event_mask == UndefinedEvents)
    469               ThrowCompareException(OptionError,"UnrecognizedEventType",
    470                 argv[i]);
    471             break;
    472           }
    473         if (LocaleCompare("decipher",option+1) == 0)
    474           {
    475             if (*option == '+')
    476               break;
    477             i++;
    478             if (i == (ssize_t) argc)
    479               ThrowCompareException(OptionError,"MissingArgument",option);
    480             break;
    481           }
    482         if (LocaleCompare("define",option+1) == 0)
    483           {
    484             i++;
    485             if (i == (ssize_t) argc)
    486               ThrowCompareException(OptionError,"MissingArgument",option);
    487             if (*option == '+')
    488               {
    489                 const char
    490                   *define;
    491 
    492                 define=GetImageOption(image_info,argv[i]);
    493                 if (define == (const char *) NULL)
    494                   ThrowCompareException(OptionError,"NoSuchOption",argv[i]);
    495                 break;
    496               }
    497             break;
    498           }
    499         if (LocaleCompare("density",option+1) == 0)
    500           {
    501             if (*option == '+')
    502               break;
    503             i++;
    504             if (i == (ssize_t) argc)
    505               ThrowCompareException(OptionError,"MissingArgument",option);
    506             if (IsGeometry(argv[i]) == MagickFalse)
    507               ThrowCompareInvalidArgumentException(option,argv[i]);
    508             break;
    509           }
    510         if (LocaleCompare("depth",option+1) == 0)
    511           {
    512             if (*option == '+')
    513               break;
    514             i++;
    515             if (i == (ssize_t) argc)
    516               ThrowCompareException(OptionError,"MissingArgument",option);
    517             if (IsGeometry(argv[i]) == MagickFalse)
    518               ThrowCompareInvalidArgumentException(option,argv[i]);
    519             break;
    520           }
    521         if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
    522           {
    523             if (*option == '+')
    524               break;
    525             i++;
    526             if (i == (ssize_t) argc)
    527               ThrowCompareException(OptionError,"MissingArgument",option);
    528             if (IsGeometry(argv[i]) == MagickFalse)
    529               ThrowCompareInvalidArgumentException(option,argv[i]);
    530             if (*option == '+')
    531               dissimilarity_threshold=DefaultDissimilarityThreshold;
    532             else
    533               dissimilarity_threshold=StringToDouble(argv[i],(char **) NULL);
    534             break;
    535           }
    536         if (LocaleCompare("duration",option+1) == 0)
    537           {
    538             if (*option == '+')
    539               break;
    540             i++;
    541             if (i == (ssize_t) argc)
    542               ThrowCompareException(OptionError,"MissingArgument",option);
    543             if (IsGeometry(argv[i]) == MagickFalse)
    544               ThrowCompareInvalidArgumentException(option,argv[i]);
    545             break;
    546           }
    547         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    548       }
    549       case 'e':
    550       {
    551         if (LocaleCompare("encipher",option+1) == 0)
    552           {
    553             if (*option == '+')
    554               break;
    555             i++;
    556             if (i == (ssize_t) argc)
    557               ThrowCompareException(OptionError,"MissingArgument",option);
    558             break;
    559           }
    560         if (LocaleCompare("extract",option+1) == 0)
    561           {
    562             if (*option == '+')
    563               break;
    564             i++;
    565             if (i == (ssize_t) argc)
    566               ThrowCompareException(OptionError,"MissingArgument",option);
    567             if (IsGeometry(argv[i]) == MagickFalse)
    568               ThrowCompareInvalidArgumentException(option,argv[i]);
    569             break;
    570           }
    571         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    572       }
    573       case 'f':
    574       {
    575         if (LocaleCompare("format",option+1) == 0)
    576           {
    577             if (*option == '+')
    578               break;
    579             i++;
    580             if (i == (ssize_t) argc)
    581               ThrowCompareException(OptionError,"MissingArgument",option);
    582             format=argv[i];
    583             break;
    584           }
    585         if (LocaleCompare("fuzz",option+1) == 0)
    586           {
    587             if (*option == '+')
    588               break;
    589             i++;
    590             if (i == (ssize_t) argc)
    591               ThrowCompareException(OptionError,"MissingArgument",option);
    592             if (IsGeometry(argv[i]) == MagickFalse)
    593               ThrowCompareInvalidArgumentException(option,argv[i]);
    594             break;
    595           }
    596         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    597       }
    598       case 'h':
    599       {
    600         if ((LocaleCompare("help",option+1) == 0) ||
    601             (LocaleCompare("-help",option+1) == 0))
    602           return(CompareUsage());
    603         if (LocaleCompare("highlight-color",option+1) == 0)
    604           {
    605             if (*option == '+')
    606               break;
    607             i++;
    608             if (i == (ssize_t) argc)
    609               ThrowCompareException(OptionError,"MissingArgument",option);
    610             break;
    611           }
    612         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    613       }
    614       case 'i':
    615       {
    616         if (LocaleCompare("identify",option+1) == 0)
    617           break;
    618         if (LocaleCompare("interlace",option+1) == 0)
    619           {
    620             ssize_t
    621               interlace;
    622 
    623             if (*option == '+')
    624               break;
    625             i++;
    626             if (i == (ssize_t) argc)
    627               ThrowCompareException(OptionError,"MissingArgument",option);
    628             interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
    629               argv[i]);
    630             if (interlace < 0)
    631               ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
    632                 argv[i]);
    633             break;
    634           }
    635         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    636       }
    637       case 'l':
    638       {
    639         if (LocaleCompare("limit",option+1) == 0)
    640           {
    641             char
    642               *p;
    643 
    644             double
    645               value;
    646 
    647             ssize_t
    648               resource;
    649 
    650             if (*option == '+')
    651               break;
    652             i++;
    653             if (i == (ssize_t) argc)
    654               ThrowCompareException(OptionError,"MissingArgument",option);
    655             resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
    656               argv[i]);
    657             if (resource < 0)
    658               ThrowCompareException(OptionError,"UnrecognizedResourceType",
    659                 argv[i]);
    660             i++;
    661             if (i == (ssize_t) argc)
    662               ThrowCompareException(OptionError,"MissingArgument",option);
    663             value=StringToDouble(argv[i],&p);
    664             (void) value;
    665             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
    666               ThrowCompareInvalidArgumentException(option,argv[i]);
    667             break;
    668           }
    669         if (LocaleCompare("list",option+1) == 0)
    670           {
    671             ssize_t
    672               list;
    673 
    674             if (*option == '+')
    675               break;
    676             i++;
    677             if (i == (ssize_t) argc)
    678               ThrowCompareException(OptionError,"MissingArgument",option);
    679             list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
    680             if (list < 0)
    681               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
    682             status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
    683               argv+j,exception);
    684             DestroyCompare();
    685             return(status == 0 ? MagickTrue : MagickFalse);
    686           }
    687         if (LocaleCompare("log",option+1) == 0)
    688           {
    689             if (*option == '+')
    690               break;
    691             i++;
    692             if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
    693               ThrowCompareException(OptionError,"MissingArgument",option);
    694             break;
    695           }
    696         if (LocaleCompare("lowlight-color",option+1) == 0)
    697           {
    698             if (*option == '+')
    699               break;
    700             i++;
    701             if (i == (ssize_t) argc)
    702               ThrowCompareException(OptionError,"MissingArgument",option);
    703             break;
    704           }
    705         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    706       }
    707       case 'm':
    708       {
    709         if (LocaleCompare("matte",option+1) == 0)
    710           break;
    711         if (LocaleCompare("metric",option+1) == 0)
    712           {
    713             ssize_t
    714               type;
    715 
    716             if (*option == '+')
    717               break;
    718             i++;
    719             if (i == (ssize_t) argc)
    720               ThrowCompareException(OptionError,"MissingArgument",option);
    721             type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
    722             if (type < 0)
    723               ThrowCompareException(OptionError,"UnrecognizedMetricType",
    724                 argv[i]);
    725             metric=(MetricType) type;
    726             break;
    727           }
    728         if (LocaleCompare("monitor",option+1) == 0)
    729           break;
    730         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    731       }
    732       case 'p':
    733       {
    734         if (LocaleCompare("profile",option+1) == 0)
    735           {
    736             i++;
    737             if (i == (ssize_t) argc)
    738               ThrowCompareException(OptionError,"MissingArgument",option);
    739             break;
    740           }
    741         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    742       }
    743       case 'q':
    744       {
    745         if (LocaleCompare("quality",option+1) == 0)
    746           {
    747             if (*option == '+')
    748               break;
    749             i++;
    750             if (i == (ssize_t) argc)
    751               ThrowCompareException(OptionError,"MissingArgument",option);
    752             if (IsGeometry(argv[i]) == MagickFalse)
    753               ThrowCompareInvalidArgumentException(option,argv[i]);
    754             break;
    755           }
    756         if (LocaleCompare("quantize",option+1) == 0)
    757           {
    758             ssize_t
    759               colorspace;
    760 
    761             if (*option == '+')
    762               break;
    763             i++;
    764             if (i == (ssize_t) argc)
    765               ThrowCompareException(OptionError,"MissingArgument",option);
    766             colorspace=ParseCommandOption(MagickColorspaceOptions,
    767               MagickFalse,argv[i]);
    768             if (colorspace < 0)
    769               ThrowCompareException(OptionError,"UnrecognizedColorspace",
    770                 argv[i]);
    771             break;
    772           }
    773         if (LocaleCompare("quiet",option+1) == 0)
    774           break;
    775         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    776       }
    777       case 'r':
    778       {
    779         if (LocaleCompare("regard-warnings",option+1) == 0)
    780           break;
    781         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
    782           {
    783             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
    784             break;
    785           }
    786         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    787       }
    788       case 's':
    789       {
    790         if (LocaleCompare("sampling-factor",option+1) == 0)
    791           {
    792             if (*option == '+')
    793               break;
    794             i++;
    795             if (i == (ssize_t) argc)
    796               ThrowCompareException(OptionError,"MissingArgument",option);
    797             if (IsGeometry(argv[i]) == MagickFalse)
    798               ThrowCompareInvalidArgumentException(option,argv[i]);
    799             break;
    800           }
    801         if (LocaleCompare("seed",option+1) == 0)
    802           {
    803             if (*option == '+')
    804               break;
    805             i++;
    806             if (i == (ssize_t) argc)
    807               ThrowCompareException(OptionError,"MissingArgument",option);
    808             if (IsGeometry(argv[i]) == MagickFalse)
    809               ThrowCompareInvalidArgumentException(option,argv[i]);
    810             break;
    811           }
    812         if (LocaleCompare("set",option+1) == 0)
    813           {
    814             i++;
    815             if (i == (ssize_t) argc)
    816               ThrowCompareException(OptionError,"MissingArgument",option);
    817             if (*option == '+')
    818               break;
    819             i++;
    820             if (i == (ssize_t) argc)
    821               ThrowCompareException(OptionError,"MissingArgument",option);
    822             break;
    823           }
    824         if (LocaleCompare("similarity-threshold",option+1) == 0)
    825           {
    826             if (*option == '+')
    827               break;
    828             i++;
    829             if (i == (ssize_t) argc)
    830               ThrowCompareException(OptionError,"MissingArgument",option);
    831             if (IsGeometry(argv[i]) == MagickFalse)
    832               ThrowCompareInvalidArgumentException(option,argv[i]);
    833             if (*option == '+')
    834               similarity_threshold=DefaultSimilarityThreshold;
    835             else
    836               similarity_threshold=StringToDouble(argv[i],(char **) NULL);
    837             break;
    838           }
    839         if (LocaleCompare("size",option+1) == 0)
    840           {
    841             if (*option == '+')
    842               break;
    843             i++;
    844             if (i == (ssize_t) argc)
    845               ThrowCompareException(OptionError,"MissingArgument",option);
    846             if (IsGeometry(argv[i]) == MagickFalse)
    847               ThrowCompareInvalidArgumentException(option,argv[i]);
    848             break;
    849           }
    850         if (LocaleCompare("subimage-search",option+1) == 0)
    851           {
    852             if (*option == '+')
    853               {
    854                 subimage_search=MagickFalse;
    855                 break;
    856               }
    857             subimage_search=MagickTrue;
    858             break;
    859           }
    860         if (LocaleCompare("synchronize",option+1) == 0)
    861           break;
    862         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    863       }
    864       case 't':
    865       {
    866         if (LocaleCompare("taint",option+1) == 0)
    867           break;
    868         if (LocaleCompare("transparent-color",option+1) == 0)
    869           {
    870             if (*option == '+')
    871               break;
    872             i++;
    873             if (i == (ssize_t) argc)
    874               ThrowCompareException(OptionError,"MissingArgument",option);
    875             break;
    876           }
    877         if (LocaleCompare("type",option+1) == 0)
    878           {
    879             ssize_t
    880               type;
    881 
    882             if (*option == '+')
    883               break;
    884             i++;
    885             if (i == (ssize_t) argc)
    886               ThrowCompareException(OptionError,"MissingArgument",option);
    887             type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
    888             if (type < 0)
    889               ThrowCompareException(OptionError,"UnrecognizedImageType",
    890                 argv[i]);
    891             break;
    892           }
    893         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    894       }
    895       case 'v':
    896       {
    897         if (LocaleCompare("verbose",option+1) == 0)
    898           break;
    899         if ((LocaleCompare("version",option+1) == 0) ||
    900             (LocaleCompare("-version",option+1) == 0))
    901           {
    902             ListMagickVersion(stdout);
    903             break;
    904           }
    905         if (LocaleCompare("virtual-pixel",option+1) == 0)
    906           {
    907             ssize_t
    908               method;
    909 
    910             if (*option == '+')
    911               break;
    912             i++;
    913             if (i == (ssize_t) argc)
    914               ThrowCompareException(OptionError,"MissingArgument",option);
    915             method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
    916               argv[i]);
    917             if (method < 0)
    918               ThrowCompareException(OptionError,
    919                 "UnrecognizedVirtualPixelMethod",argv[i]);
    920             break;
    921           }
    922         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    923       }
    924       case '?':
    925         break;
    926       default:
    927         ThrowCompareException(OptionError,"UnrecognizedOption",option)
    928     }
    929     fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
    930       FireOptionFlag) == 0 ?  MagickFalse : MagickTrue;
    931     if (fire != MagickFalse)
    932       FireImageStack(MagickTrue,MagickTrue,MagickTrue);
    933   }
    934   if (k != 0)
    935     ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
    936   if (i-- != (ssize_t) (argc-1))
    937     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
    938   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
    939     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
    940   FinalizeImageSettings(image_info,image,MagickTrue);
    941   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
    942     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
    943   image=GetImageFromList(image,0);
    944   reconstruct_image=GetImageFromList(image,1);
    945   offset.x=0;
    946   offset.y=0;
    947   if (subimage_search != MagickFalse)
    948     {
    949       similarity_image=SimilarityImage(image,reconstruct_image,metric,
    950         similarity_threshold,&offset,&similarity_metric,exception);
    951       if (similarity_metric > dissimilarity_threshold)
    952         ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
    953     }
    954   if ((reconstruct_image->columns == image->columns) &&
    955        (reconstruct_image->rows == image->rows))
    956     difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
    957       exception);
    958   else
    959     if (similarity_image == (Image *) NULL)
    960       {
    961         if (metric == PerceptualHashErrorMetric)
    962           difference_image=CompareImages(image,reconstruct_image,metric,
    963             &distortion,exception);
    964         else
    965           ThrowCompareException(OptionError,"ImageWidthsOrHeightsDiffer",
    966             image->filename);
    967       }
    968     else
    969       {
    970         Image
    971           *composite_image;
    972 
    973         /*
    974           Determine if reconstructed image is a subimage of the image.
    975         */
    976         composite_image=CloneImage(image,0,0,MagickTrue,exception);
    977         if (composite_image == (Image *) NULL)
    978           difference_image=CompareImages(image,reconstruct_image,metric,
    979             &distortion,exception);
    980         else
    981           {
    982             Image
    983               *distort_image;
    984 
    985             RectangleInfo
    986               page;
    987 
    988             (void) CompositeImage(composite_image,reconstruct_image,
    989               CopyCompositeOp,MagickTrue,offset.x,offset.y,exception);
    990             difference_image=CompareImages(image,composite_image,metric,
    991               &distortion,exception);
    992             if (difference_image != (Image *) NULL)
    993               {
    994                 difference_image->page.x=offset.x;
    995                 difference_image->page.y=offset.y;
    996               }
    997             composite_image=DestroyImage(composite_image);
    998             page.width=reconstruct_image->columns;
    999             page.height=reconstruct_image->rows;
   1000             page.x=offset.x;
   1001             page.y=offset.y;
   1002             distort_image=CropImage(image,&page,exception);
   1003             if (distort_image != (Image *) NULL)
   1004               {
   1005                 Image
   1006                   *sans_image;
   1007 
   1008                 sans_image=CompareImages(distort_image,reconstruct_image,metric,
   1009                   &distortion,exception);
   1010                 distort_image=DestroyImage(distort_image);
   1011                 if (sans_image != (Image *) NULL)
   1012                   sans_image=DestroyImage(sans_image);
   1013               }
   1014           }
   1015         if (difference_image != (Image *) NULL)
   1016           {
   1017             AppendImageToList(&difference_image,similarity_image);
   1018             similarity_image=(Image *) NULL;
   1019           }
   1020       }
   1021   if (difference_image == (Image *) NULL)
   1022     status=0;
   1023   else
   1024     {
   1025       if (image_info->verbose != MagickFalse)
   1026         (void) SetImageColorMetric(image,reconstruct_image,exception);
   1027       if (*difference_image->magick == '\0')
   1028         (void) CopyMagickString(difference_image->magick,image->magick,
   1029           MagickPathExtent);
   1030       if (image_info->verbose == MagickFalse)
   1031         {
   1032           switch (metric)
   1033           {
   1034             case FuzzErrorMetric:
   1035             case MeanAbsoluteErrorMetric:
   1036             case MeanSquaredErrorMetric:
   1037             case PeakAbsoluteErrorMetric:
   1038             case RootMeanSquaredErrorMetric:
   1039             {
   1040               (void) FormatLocaleFile(stderr,"%g (%g)",QuantumRange*distortion,
   1041                 (double) distortion);
   1042               break;
   1043             }
   1044             case AbsoluteErrorMetric:
   1045             case NormalizedCrossCorrelationErrorMetric:
   1046             case PeakSignalToNoiseRatioErrorMetric:
   1047             case PerceptualHashErrorMetric:
   1048             {
   1049               (void) FormatLocaleFile(stderr,"%g",distortion);
   1050               break;
   1051             }
   1052             case MeanErrorPerPixelErrorMetric:
   1053             {
   1054               (void) FormatLocaleFile(stderr,"%g (%g, %g)",distortion,
   1055                 image->error.normalized_mean_error,
   1056                 image->error.normalized_maximum_error);
   1057               break;
   1058             }
   1059             case UndefinedErrorMetric:
   1060               break;
   1061           }
   1062           if (subimage_search != MagickFalse)
   1063             (void) FormatLocaleFile(stderr," @ %.20g,%.20g",(double)
   1064               difference_image->page.x,(double) difference_image->page.y);
   1065         }
   1066       else
   1067         {
   1068           double
   1069             *channel_distortion;
   1070 
   1071           channel_distortion=GetImageDistortions(image,reconstruct_image,
   1072             metric,exception);
   1073           (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
   1074           if ((reconstruct_image->columns != image->columns) ||
   1075               (reconstruct_image->rows != image->rows))
   1076             (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
   1077               difference_image->page.x,(double) difference_image->page.y);
   1078           (void) FormatLocaleFile(stderr,"  Channel distortion: %s\n",
   1079             CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
   1080           switch (metric)
   1081           {
   1082             case FuzzErrorMetric:
   1083             case MeanAbsoluteErrorMetric:
   1084             case MeanSquaredErrorMetric:
   1085             case PeakAbsoluteErrorMetric:
   1086             case RootMeanSquaredErrorMetric:
   1087             {
   1088               switch (image->colorspace)
   1089               {
   1090                 case RGBColorspace:
   1091                 default:
   1092                 {
   1093                   (void) FormatLocaleFile(stderr,"    red: %g (%g)\n",
   1094                     QuantumRange*channel_distortion[RedPixelChannel],
   1095                     channel_distortion[RedPixelChannel]);
   1096                   (void) FormatLocaleFile(stderr,"    green: %g (%g)\n",
   1097                     QuantumRange*channel_distortion[GreenPixelChannel],
   1098                     channel_distortion[GreenPixelChannel]);
   1099                   (void) FormatLocaleFile(stderr,"    blue: %g (%g)\n",
   1100                     QuantumRange*channel_distortion[BluePixelChannel],
   1101                     channel_distortion[BluePixelChannel]);
   1102                   if (image->alpha_trait != UndefinedPixelTrait)
   1103                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
   1104                       QuantumRange*channel_distortion[AlphaPixelChannel],
   1105                       channel_distortion[AlphaPixelChannel]);
   1106                   break;
   1107                 }
   1108                 case CMYKColorspace:
   1109                 {
   1110                   (void) FormatLocaleFile(stderr,"    cyan: %g (%g)\n",
   1111                     QuantumRange*channel_distortion[CyanPixelChannel],
   1112                     channel_distortion[CyanPixelChannel]);
   1113                   (void) FormatLocaleFile(stderr,"    magenta: %g (%g)\n",
   1114                     QuantumRange*channel_distortion[MagentaPixelChannel],
   1115                     channel_distortion[MagentaPixelChannel]);
   1116                   (void) FormatLocaleFile(stderr,"    yellow: %g (%g)\n",
   1117                     QuantumRange*channel_distortion[YellowPixelChannel],
   1118                     channel_distortion[YellowPixelChannel]);
   1119                   (void) FormatLocaleFile(stderr,"    black: %g (%g)\n",
   1120                     QuantumRange*channel_distortion[BlackPixelChannel],
   1121                     channel_distortion[BlackPixelChannel]);
   1122                   if (image->alpha_trait != UndefinedPixelTrait)
   1123                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
   1124                       QuantumRange*channel_distortion[AlphaPixelChannel],
   1125                       channel_distortion[AlphaPixelChannel]);
   1126                   break;
   1127                 }
   1128                 case GRAYColorspace:
   1129                 {
   1130                   (void) FormatLocaleFile(stderr,"    gray: %g (%g)\n",
   1131                     QuantumRange*channel_distortion[GrayPixelChannel],
   1132                     channel_distortion[GrayPixelChannel]);
   1133                   if (image->alpha_trait != UndefinedPixelTrait)
   1134                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
   1135                       QuantumRange*channel_distortion[AlphaPixelChannel],
   1136                       channel_distortion[AlphaPixelChannel]);
   1137                   break;
   1138                 }
   1139               }
   1140               (void) FormatLocaleFile(stderr,"    all: %g (%g)\n",
   1141                 QuantumRange*channel_distortion[MaxPixelChannels],
   1142                 channel_distortion[MaxPixelChannels]);
   1143               break;
   1144             }
   1145             case AbsoluteErrorMetric:
   1146             case NormalizedCrossCorrelationErrorMetric:
   1147             case PeakSignalToNoiseRatioErrorMetric:
   1148             case PerceptualHashErrorMetric:
   1149             {
   1150               switch (image->colorspace)
   1151               {
   1152                 case RGBColorspace:
   1153                 default:
   1154                 {
   1155                   (void) FormatLocaleFile(stderr,"    red: %g\n",
   1156                     channel_distortion[RedPixelChannel]);
   1157                   (void) FormatLocaleFile(stderr,"    green: %g\n",
   1158                     channel_distortion[GreenPixelChannel]);
   1159                   (void) FormatLocaleFile(stderr,"    blue: %g\n",
   1160                     channel_distortion[BluePixelChannel]);
   1161                   if (image->alpha_trait != UndefinedPixelTrait)
   1162                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
   1163                       channel_distortion[AlphaPixelChannel]);
   1164                   break;
   1165                 }
   1166                 case CMYKColorspace:
   1167                 {
   1168                   (void) FormatLocaleFile(stderr,"    cyan: %g\n",
   1169                     channel_distortion[CyanPixelChannel]);
   1170                   (void) FormatLocaleFile(stderr,"    magenta: %g\n",
   1171                     channel_distortion[MagentaPixelChannel]);
   1172                   (void) FormatLocaleFile(stderr,"    yellow: %g\n",
   1173                     channel_distortion[YellowPixelChannel]);
   1174                   (void) FormatLocaleFile(stderr,"    black: %g\n",
   1175                     channel_distortion[BlackPixelChannel]);
   1176                   if (image->alpha_trait != UndefinedPixelTrait)
   1177                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
   1178                       channel_distortion[AlphaPixelChannel]);
   1179                   break;
   1180                 }
   1181                 case GRAYColorspace:
   1182                 {
   1183                   (void) FormatLocaleFile(stderr,"    gray: %g\n",
   1184                     channel_distortion[GrayPixelChannel]);
   1185                   if (image->alpha_trait != UndefinedPixelTrait)
   1186                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
   1187                       channel_distortion[AlphaPixelChannel]);
   1188                   break;
   1189                 }
   1190               }
   1191               (void) FormatLocaleFile(stderr,"    all: %g\n",
   1192                 channel_distortion[MaxPixelChannels]);
   1193               break;
   1194             }
   1195             case MeanErrorPerPixelErrorMetric:
   1196             {
   1197               (void) FormatLocaleFile(stderr,"    %g (%g, %g)\n",
   1198                 channel_distortion[MaxPixelChannels],
   1199                 image->error.normalized_mean_error,
   1200                 image->error.normalized_maximum_error);
   1201               break;
   1202             }
   1203             case UndefinedErrorMetric:
   1204               break;
   1205           }
   1206           channel_distortion=(double *) RelinquishMagickMemory(
   1207             channel_distortion);
   1208           if (subimage_search != MagickFalse)
   1209             (void) FormatLocaleFile(stderr,"   Offset: %.20g,%.20g\n",(double)
   1210               difference_image->page.x,(double) difference_image->page.y);
   1211         }
   1212       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
   1213       if ((metadata != (char **) NULL) && (format != (char *) NULL))
   1214         {
   1215           char
   1216             *text;
   1217 
   1218           text=InterpretImageProperties(image_info,difference_image,format,
   1219             exception);
   1220           if (text == (char *) NULL)
   1221             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
   1222               GetExceptionMessage(errno));
   1223           (void) ConcatenateString(&(*metadata),text);
   1224           text=DestroyString(text);
   1225         }
   1226       difference_image=DestroyImageList(difference_image);
   1227     }
   1228   DestroyCompare();
   1229   if ((metric == NormalizedCrossCorrelationErrorMetric) ||
   1230       (metric == UndefinedErrorMetric))
   1231     {
   1232       if (fabs(distortion-1.0) > CompareEpsilon)
   1233         (void) SetImageOption(image_info,"compare:dissimilar","true");
   1234     }
   1235   else
   1236     if (fabs(distortion) > CompareEpsilon)
   1237       (void) SetImageOption(image_info,"compare:dissimilar","true");
   1238   return(status != 0 ? MagickTrue : MagickFalse);
   1239 }
   1240