root / ImageMagick / branches / ImageMagick-6.3.5 / magick / compare.c

Revision 7935, 29.9 kB (checked in by anthony, 15 months ago)

Adjustment to last check in

Line 
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%                                John Cristy                                  %
17%                               December 2003                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2007 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%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/cache-view.h"
45#include "magick/client.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
48#include "magick/colorspace.h"
49#include "magick/colorspace-private.h"
50#include "magick/compare.h"
51#include "magick/composite-private.h"
52#include "magick/constitute.h"
53#include "magick/exception-private.h"
54#include "magick/geometry.h"
55#include "magick/image-private.h"
56#include "magick/list.h"
57#include "magick/log.h"
58#include "magick/memory_.h"
59#include "magick/option.h"
60#include "magick/pixel-private.h"
61#include "magick/resource_.h"
62#include "magick/string_.h"
63#include "magick/utility.h"
64#include "magick/version.h"
65
66/*
67%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68%                                                                             %
69%                                                                             %
70%                                                                             %
71%   C o m p a r e I m a g e C h a n n e l s                                   %
72%                                                                             %
73%                                                                             %
74%                                                                             %
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76%
77%  CompareImageChannels() compares one or more image channels of an image
78%  to a reconstructed image and returns the difference image.
79%
80%  The format of the CompareImageChannels method is:
81%
82%      Image *CompareImageChannels(const Image *image,
83%        const Image *reconstruct_image,const ChannelType channel,
84%        const MetricType metric,double *distortion,ExceptionInfo *exception)
85%
86%  A description of each parameter follows:
87%
88%    o image: The image.
89%
90%    o reconstruct_image: The reconstruct image.
91%
92%    o channel: The channel.
93%
94%    o metric: The metric.
95%
96%    o distortion: The computed distortion between the images.
97%
98%    o exception: Return any errors or warnings in this structure.
99%
100*/
101
102MagickExport Image *CompareImages(Image *image,const Image *reconstruct_image,
103  const MetricType metric,double *distortion,ExceptionInfo *exception)
104{
105  Image
106    *difference_image;
107
108  difference_image=CompareImageChannels(image,reconstruct_image,AllChannels,
109    metric,distortion,exception);
110  return(difference_image);
111}
112
113MagickExport Image *CompareImageChannels(Image *image,
114  const Image *reconstruct_image,const ChannelType channel,
115  const MetricType metric,double *distortion,ExceptionInfo *exception)
116{
117  Image
118    *difference_image;
119
120  long
121    y;
122
123  MagickPixelPacket
124    composite,
125    red,
126    source,
127    white;
128
129  MagickStatusType
130    difference;
131
132  register const IndexPacket
133    *indexes,
134    *reconstruct_indexes;
135
136  register const PixelPacket
137    *p,
138    *q;
139
140  register IndexPacket
141    *difference_indexes;
142
143  register long
144    x;
145
146  register PixelPacket
147    *r;
148
149  ViewInfo
150    *difference_view,
151    *image_view,
152    *reconstruct_view;
153
154  assert(image != (Image *) NULL);
155  assert(image->signature == MagickSignature);
156  if (image->debug != MagickFalse)
157    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
158  assert(reconstruct_image != (const Image *) NULL);
159  assert(reconstruct_image->signature == MagickSignature);
160  assert(distortion != (double *) NULL);
161  *distortion=0.0;
162  if (image->debug != MagickFalse)
163    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
164  if ((reconstruct_image->columns != image->columns) ||
165      (reconstruct_image->rows != image->rows))
166    ThrowImageException(ImageError,"ImageSizeDiffers");
167  difference_image=CloneImage(image,image->columns,image->rows,MagickTrue,
168    exception);
169  if (difference_image == (Image *) NULL)
170    return((Image *) NULL);
171  if (SetImageStorageClass(difference_image,DirectClass) == MagickFalse)
172    {
173      InheritException(exception,&difference_image->exception);
174      difference_image=DestroyImage(difference_image);
175      return((Image *) NULL);
176    }
177  (void) QueryMagickColor("#f1001e",&red,exception);
178  (void) QueryMagickColor("#ffffff",&white,exception);
179  if (difference_image->colorspace == CMYKColorspace)
180    {
181      ConvertRGBToCMYK(&red);
182      ConvertRGBToCMYK(&white);
183    }
184  /*
185    Generate difference image.
186  */
187  GetMagickPixelPacket(reconstruct_image,&source);
188  GetMagickPixelPacket(difference_image,&composite);
189  image_view=OpenCacheView(image);
190  reconstruct_view=OpenCacheView(reconstruct_image);
191  difference_view=OpenCacheView(difference_image);
192  for (y=0; y < (long) image->rows; y++)
193  {
194    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
195    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
196      exception);
197    r=SetCacheView(difference_view,0,y,difference_image->columns,1);
198    if ((p == (const PixelPacket *) NULL) ||
199        (q == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
200      break;
201    indexes=AcquireCacheViewIndexes(image_view);
202    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
203    difference_indexes=GetCacheViewIndexes(difference_view);
204    for (x=0; x < (long) image->columns; x++)
205    {
206      difference=MagickFalse;
207      if ((channel & RedChannel) != 0)
208        if (p->red != q->red)
209          difference=MagickTrue;
210      if ((channel & GreenChannel) != 0)
211        if (p->green != q->green)
212          difference=MagickTrue;
213      if ((channel & BlueChannel) != 0)
214        if (p->blue != q->blue)
215          difference=MagickTrue;
216      if ((channel & OpacityChannel) != 0)
217        if (p->opacity != q->opacity)
218          difference=MagickTrue;
219      if (((channel & IndexChannel) != 0) &&
220          (image->colorspace == CMYKColorspace) &&
221          (reconstruct_image->colorspace == CMYKColorspace))
222        if (indexes[x] != reconstruct_indexes[x])
223          difference=MagickTrue;
224      SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,&source);
225      if (difference != MagickFalse)
226        MagickPixelCompositeOver(&source,7.5*QuantumRange/10.0,&red,
227          (MagickRealType) red.opacity,&composite);
228      else
229        MagickPixelCompositeOver(&source,7.5*QuantumRange/10.0,&white,
230          (MagickRealType) white.opacity,&composite);
231      SetPixelPacket(difference_image,&composite,r,difference_indexes+x);
232      p++;
233      q++;
234      r++;
235    }
236    if (SyncCacheView(difference_view) == MagickFalse)
237      break;
238  }
239  difference_view=CloseCacheView(difference_view);
240  reconstruct_view=CloseCacheView(reconstruct_view);
241  image_view=CloseCacheView(image_view);
242  (void) GetImageChannelDistortion(image,reconstruct_image,channel,metric,
243    distortion,exception);
244  return(difference_image);
245}
246
247/*
248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249%                                                                             %
250%                                                                             %
251%                                                                             %
252%   G e t I m a g e C h a n n e l D i s t o r t i o n                         %
253%                                                                             %
254%                                                                             %
255%                                                                             %
256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257%
258%  GetImageChannelDistrortion() compares one or more image channels of an image
259%  to a reconstructed image and returns the specified distortion metric.
260%
261%  The format of the CompareImageChannels method is:
262%
263%      MagickBooleanType GetImageChhannelDistortion(const Image *image,
264%        const Image *reconstruct_image,const ChannelType channel,
265%        const MetricType metric,double *distortion,ExceptionInfo *exception)
266%
267%  A description of each parameter follows:
268%
269%    o image: The image.
270%
271%    o reconstruct_image: The reconstruct image.
272%
273%    o channel: The channel.
274%
275%    o metric: The metric.
276%
277%    o distortion: The computed distortion between the images.
278%
279%    o exception: Return any errors or warnings in this structure.
280%
281*/
282
283MagickExport MagickBooleanType GetImageDistortion(Image *image,
284  const Image *reconstruct_image,const MetricType metric,double *distortion,
285  ExceptionInfo *exception)
286{
287  MagickBooleanType
288    status;
289
290  status=GetImageChannelDistortion(image,reconstruct_image,AllChannels,
291    metric,distortion,exception);
292  return(status);
293}
294
295static MagickRealType GetAbsoluteError(const Image *image,
296  const Image *reconstruct_image,ExceptionInfo *exception)
297{
298  long
299    y;
300
301  MagickPixelPacket
302    image_pixel,
303    reconstruct_pixel;
304
305  MagickRealType
306    distortion;
307
308  register const IndexPacket
309    *indexes,
310    *reconstruct_indexes;
311
312  register const PixelPacket
313    *p,
314    *q;
315
316  register long
317    x;
318
319  ViewInfo
320    *image_view,
321    *reconstruct_view;
322
323  /*
324    Compute the absolute difference in pixels between two images.
325  */
326  GetMagickPixelPacket(image,&image_pixel);
327  GetMagickPixelPacket(reconstruct_image,&reconstruct_pixel);
328  distortion=0.0;
329  image_view=OpenCacheView(image);
330  reconstruct_view=OpenCacheView(reconstruct_image);
331  for (y=0; y < (long) image->rows; y++)
332  {
333    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
334    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
335      exception);
336    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
337      break;
338    indexes=AcquireCacheViewIndexes(image_view);
339    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
340    for (x=0; x < (long) image->columns; x++)
341    {
342      SetMagickPixelPacket(image,p,indexes+x,&image_pixel);
343      SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
344        &reconstruct_pixel);
345      if (IsMagickColorSimilar(&image_pixel,&reconstruct_pixel) == MagickFalse)
346        distortion++;
347      p++;
348      q++;
349    }
350  }
351  reconstruct_view=CloseCacheView(reconstruct_view);
352  image_view=CloseCacheView(image_view);
353  return(distortion);
354}
355
356static MagickRealType GetMeanAbsoluteError(const Image *image,
357  const Image *reconstruct_image,const ChannelType channel,
358  ExceptionInfo *exception)
359{
360  long
361    y;
362
363  MagickRealType
364    area,
365    distortion;
366
367  register const IndexPacket
368    *indexes,
369    *reconstruct_indexes;
370
371  register const PixelPacket
372    *p,
373    *q;
374
375  register long
376    x;
377
378  ViewInfo
379    *image_view,
380    *reconstruct_view;
381
382  area=0.0;
383  distortion=0.0;
384  image_view=OpenCacheView(image);
385  reconstruct_view=OpenCacheView(reconstruct_image);
386  for (y=0; y < (long) image->rows; y++)
387  {
388    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
389    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
390      exception);
391    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
392      break;
393    indexes=AcquireCacheViewIndexes(image_view);
394    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
395    for (x=0; x < (long) image->columns; x++)
396    {
397      if ((channel & RedChannel) != 0)
398        {
399          distortion+=fabs(p->red-(double) q->red);
400          area++;
401        }
402      if ((channel & GreenChannel) != 0)
403        {
404          distortion+=fabs(p->green-(double) q->green);
405          area++;
406        }
407      if ((channel & BlueChannel) != 0)
408        {
409          distortion+=fabs(p->blue-(double) q->blue);
410          area++;
411        }
412      if ((channel & OpacityChannel) != 0)
413        {
414          distortion+=fabs(p->opacity-(double) q->opacity);
415          area++;
416        }
417      if (((channel & IndexChannel) != 0) &&
418          (image->colorspace == CMYKColorspace))
419        {
420          distortion+=fabs(indexes[x]-(double) reconstruct_indexes[x]);
421          area++;
422        }
423      p++;
424      q++;
425    }
426  }
427  reconstruct_view=CloseCacheView(reconstruct_view);
428  image_view=CloseCacheView(image_view);
429  return(distortion/area);
430}
431
432static MagickRealType GetMeanErrorPerPixel(Image *image,
433  const Image *reconstruct_image,const ChannelType channel,
434  ExceptionInfo *exception)
435{
436  double
437    alpha,
438    area,
439    beta,
440    distance,
441    maximum_error,
442    mean_error,
443    mean_error_per_pixel;
444
445  long
446    y;
447
448  register const IndexPacket
449    *indexes,
450    *reconstruct_indexes;
451
452  register const PixelPacket
453    *p,
454    *q;
455
456  register long
457    x;
458
459  ViewInfo
460    *image_view,
461    *reconstruct_view;
462
463  alpha=1.0;
464  beta=1.0;
465  area=0.0;
466  maximum_error=0.0;
467  mean_error_per_pixel=0.0;
468  mean_error=0.0;
469  image_view=OpenCacheView(image);
470  reconstruct_view=OpenCacheView(reconstruct_image);
471  for (y=0; y < (long) image->rows; y++)
472  {
473    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
474    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
475      exception);
476    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
477      break;
478    indexes=AcquireCacheViewIndexes(image_view);
479    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
480    for (x=0; x < (long) image->columns; x++)
481    {
482      if ((channel & OpacityChannel) != 0)
483        {
484          if (image->matte != MagickFalse)
485            alpha=QuantumScale*(QuantumRange-(double) p->opacity);
486          if (reconstruct_image->matte != MagickFalse)
487            beta=QuantumScale*(QuantumRange-(double) q->opacity);
488        }
489      if ((channel & RedChannel) != 0)
490        {
491          distance=fabs(alpha*p->red-beta*q->red);
492          mean_error_per_pixel+=distance;
493          mean_error+=distance*distance;
494          if (distance > maximum_error)
495            maximum_error=distance;
496          area++;
497        }
498      if ((channel & GreenChannel) != 0)
499        {
500          distance=fabs(alpha*p->green-beta*q->green);
501          mean_error_per_pixel+=distance;
502          mean_error+=distance*distance;
503          if (distance > maximum_error)
504            maximum_error=distance;
505          area++;
506        }
507      if ((channel & BlueChannel) != 0)
508        {
509          distance=fabs(alpha*p->blue-beta*q->blue);
510          mean_error_per_pixel+=distance;
511          mean_error+=distance*distance;
512          if (distance > maximum_error)
513            maximum_error=distance;
514          area++;
515        }
516      if ((channel & OpacityChannel) != 0)
517        {
518          distance=fabs(alpha*p->opacity-beta*q->opacity);
519          mean_error_per_pixel+=distance;
520          mean_error+=distance*distance;
521          if (distance > maximum_error)
522            maximum_error=distance;
523          area++;
524        }
525      if (((channel & IndexChannel) != 0) &&
526          (image->colorspace == CMYKColorspace) &&
527          (reconstruct_image->colorspace == CMYKColorspace))
528        {
529          distance=fabs(alpha*indexes[x]-beta*reconstruct_indexes[x]);
530          mean_error_per_pixel+=distance;
531          mean_error+=distance*distance;
532          if (distance > maximum_error)
533            maximum_error=distance;
534          area++;
535        }
536      p++;
537      q++;
538    }
539  }
540  reconstruct_view=CloseCacheView(reconstruct_view);
541  image_view=CloseCacheView(image_view);
542  image->error.mean_error_per_pixel=mean_error_per_pixel/area;
543  image->error.normalized_mean_error=QuantumScale*QuantumScale*mean_error/area;
544  image->error.normalized_maximum_error=QuantumScale*maximum_error;
545  return((MagickRealType) image->error.mean_error_per_pixel);
546}
547
548static MagickRealType GetMeanSquaredError(const Image *image,
549  const Image *reconstruct_image,const ChannelType channel,
550  ExceptionInfo *exception)
551{
552  long
553    y;
554
555  MagickRealType
556    area,
557    distance,
558    distortion;
559
560  register const IndexPacket
561    *indexes,
562    *reconstruct_indexes;
563
564  register const PixelPacket
565    *p,
566    *q;
567
568  register long
569    x;
570
571  ViewInfo
572    *image_view,
573    *reconstruct_view;
574
575  area=0.0;
576  distortion=0.0;
577  image_view=OpenCacheView(image);
578  reconstruct_view=OpenCacheView(reconstruct_image);
579  for (y=0; y < (long) image->rows; y++)
580  {
581    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
582    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
583      exception);
584    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
585      break;
586    indexes=AcquireCacheViewIndexes(image_view);
587    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
588    for (x=0; x < (long) image->columns; x++)
589    {
590      if ((channel & RedChannel) != 0)
591        {
592          distance=p->red-(MagickRealType) q->red;
593          distortion+=distance*distance;
594          area++;
595        }
596      if ((channel & GreenChannel) != 0)
597        {
598          distance=p->green-(MagickRealType) q->green;
599          distortion+=distance*distance;
600          area++;
601        }
602      if ((channel & BlueChannel) != 0)
603        {
604          distance=p->blue-(MagickRealType) q->blue;
605          distortion+=distance*distance;
606          area++;
607        }
608      if ((channel & OpacityChannel) != 0)
609        {
610          distance=p->opacity-(MagickRealType) q->opacity;
611          distortion+=distance*distance;
612          area++;
613        }
614      if (((channel & IndexChannel) != 0) &&
615          (image->colorspace == CMYKColorspace) &&
616          (reconstruct_image->colorspace == CMYKColorspace))
617        {
618          distance=indexes[x]-(MagickRealType) reconstruct_indexes[x];
619          distortion+=distance*distance;
620          area++;
621        }
622      p++;
623      q++;
624    }
625  }
626  reconstruct_view=CloseCacheView(reconstruct_view);
627  image_view=CloseCacheView(image_view);
628  return(distortion/area);
629}
630
631static MagickRealType GetPeakAbsoluteError(const Image *image,
632  const Image *reconstruct_image,const ChannelType channel,
633  ExceptionInfo *exception)
634{
635  long
636    y;
637
638  MagickRealType
639    distance,
640    distortion;
641
642  register const IndexPacket
643    *indexes,
644    *reconstruct_indexes;
645
646  register const PixelPacket
647    *p,
648    *q;
649
650  register long
651    x;
652
653  ViewInfo
654    *image_view,
655    *reconstruct_view;
656
657  distortion=0.0;
658  image_view=OpenCacheView(image);
659  reconstruct_view=OpenCacheView(reconstruct_image);
660  for (y=0; y < (long) image->rows; y++)
661  {
662    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception);
663    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
664      exception);
665    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
666      break;
667    indexes=AcquireCacheViewIndexes(image_view);
668    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
669    for (x=0; x < (long) image->columns; x++)
670    {
671      if ((channel & RedChannel) != 0)
672        {
673          distance=fabs(p->red-(double) q->red);
674          if (distance > distortion)
675            distortion=distance;
676        }
677      if ((channel & GreenChannel) != 0)
678        {
679          distance=fabs(p->green-(double) q->green);
680          if (distance > distortion)
681            distortion=distance;
682        }
683      if ((channel & BlueChannel) != 0)
684        {
685          distance=fabs(p->blue-(double) q->blue);
686          if (distance > distortion)
687            distortion=distance;
688        }
689      if ((channel & OpacityChannel) != 0)
690        {
691          distance=fabs(p->opacity-(double) q->opacity);
692          if (distance > distortion)
693            distortion=distance;
694        }
695      if (((channel & IndexChannel) != 0) &&
696          (image->colorspace == CMYKColorspace) &&
697          (reconstruct_image->colorspace == CMYKColorspace))
698        {
699          distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
700          if (distance > distortion)
701            distortion=distance;
702        }
703      p++;
704      q++;
705    }
706  }
707  reconstruct_view=CloseCacheView(reconstruct_view);
708  image_view=CloseCacheView(image_view);
709  return(distortion);
710}
711
712static MagickRealType GetPeakSignalToNoiseRatio(const Image *image,
713  const Image *reconstruct_image,const ChannelType channel,
714  ExceptionInfo *exception)
715{
716  MagickRealType
717    distortion;
718
719  distortion=GetMeanSquaredError(image,reconstruct_image,channel,exception);
720  return(20.0*log10((double) QuantumRange/sqrt((double) distortion)));
721}
722
723static MagickRealType GetRootMeanSquaredError(const Image *image,
724  const Image *reconstruct_image,const ChannelType channel,
725  ExceptionInfo *exception)
726{
727  MagickRealType
728    distortion;
729
730  distortion=GetMeanSquaredError(image,reconstruct_image,channel,exception);
731  return(sqrt((double) distortion));
732}
733
734MagickExport MagickBooleanType GetImageChannelDistortion(Image *image,
735  const Image *reconstruct_image,const ChannelType channel,
736  const MetricType metric,double *distortion,ExceptionInfo *exception)
737{
738  assert(image != (Image *) NULL);
739  assert(image->signature == MagickSignature);
740  if (image->debug != MagickFalse)
741    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
742  assert(reconstruct_image != (const Image *) NULL);
743  assert(reconstruct_image->signature == MagickSignature);
744  assert(distortion != (double *) NULL);
745  *distortion=0.0;
746  if (image->debug != MagickFalse)
747    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
748  if ((reconstruct_image->columns != image->columns) ||
749      (reconstruct_image->rows != image->rows))
750    ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
751  /*
752    Get image distortion.
753  */
754  switch (metric)
755  {
756    case AbsoluteErrorMetric:
757    {
758      *distortion=(double) GetAbsoluteError(image,reconstruct_image,exception);
759      break;
760    }
761    case MeanAbsoluteErrorMetric:
762    {
763      *distortion=(double) GetMeanAbsoluteError(image,reconstruct_image,channel,
764        exception);
765      break;
766    }
767    case MeanErrorPerPixelMetric:
768    {
769      *distortion=(double) GetMeanErrorPerPixel(image,reconstruct_image,channel,
770        exception);
771      break;
772    }
773    case MeanSquaredErrorMetric:
774    {
775      *distortion=(double) GetMeanSquaredError(image,reconstruct_image,channel,
776        exception);
777      break;
778    }
779    case PeakAbsoluteErrorMetric:
780    default:
781    {
782      *distortion=(double) GetPeakAbsoluteError(image,reconstruct_image,channel,
783        exception);
784      break;
785    }
786    case PeakSignalToNoiseRatioMetric:
787    {
788      *distortion=(double) GetPeakSignalToNoiseRatio(image,reconstruct_image,
789        channel,exception);
790      break;
791    }
792    case RootMeanSquaredErrorMetric:
793    {
794      *distortion=(double) GetRootMeanSquaredError(image,reconstruct_image,
795        channel,exception);
796      break;
797    }
798  }
799  return(MagickTrue);
800}
801
802/*
803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804%                                                                             %
805%                                                                             %
806%                                                                             %
807%  I s I m a g e s E q u a l                                                  %
808%                                                                             %
809%                                                                             %
810%                                                                             %
811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812%
813%  IsImagesEqual() measures the difference between colors at each pixel
814%  location of two images.  A value other than 0 means the colors match
815%  exactly.  Otherwise an error measure is computed by summing over all
816%  pixels in an image the distance squared in RGB space between each image
817%  pixel and its corresponding pixel in the reconstruct image.  The error
818%  measure is assigned to these image members:
819%
820%    o mean_error_per_pixel:  The mean error for any single pixel in
821%      the image.
822%
823%    o normalized_mean_error:  The normalized mean quantization error for
824%      any single pixel in the image.  This distance measure is normalized to
825%      a range between 0 and 1.  It is independent of the range of red, green,
826%      and blue values in the image.
827%
828%    o normalized_maximum_error:  The normalized maximum quantization
829%      error for any single pixel in the image.  This distance measure is
830%      normalized to a range between 0 and 1.  It is independent of the range
831%      of red, green, and blue values in your image.
832%
833%  A small normalized mean square error, accessed as
834%  image->normalized_mean_error, suggests the images are very similar in
835%  spatial layout and color.
836%
837%  The format of the IsImagesEqual method is:
838%
839%      MagickBooleanType IsImagesEqual(Image *image,
840%        const Image *reconstruct_image)
841%
842%  A description of each parameter follows.
843%
844%    o image: The image.
845%
846%    o reconstruct_image: The reconstruct image.
847%
848*/
849MagickExport MagickBooleanType IsImagesEqual(Image *image,
850  const Image *reconstruct_image)
851{
852  long
853    y;
854
855  MagickBooleanType
856    status;
857
858  MagickRealType
859    area,
860    distance,
861    maximum_error,
862    mean_error,
863    mean_error_per_pixel;
864
865  register const IndexPacket
866    *indexes,
867    *reconstruct_indexes;
868
869  register const PixelPacket
870    *p,
871    *q;
872
873  register long
874    x;
875
876  ViewInfo
877    *image_view,
878    *reconstruct_view;
879
880  assert(image != (Image *) NULL);
881  assert(image->signature == MagickSignature);
882  assert(reconstruct_image != (const Image *) NULL);
883  assert(reconstruct_image->signature == MagickSignature);
884  if ((reconstruct_image->columns != image->columns) ||
885      (reconstruct_image->rows != image->rows))
886    ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
887  area=0.0;
888  maximum_error=0.0;
889  mean_error_per_pixel=0.0;
890  mean_error=0.0;
891  image_view=OpenCacheView(image);
892  reconstruct_view=OpenCacheView(reconstruct_image);
893  for (y=0; y < (long) image->rows; y++)
894  {
895    p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,&image->exception);
896    q=AcquireCacheViewPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
897      &image->exception);
898    if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
899      break;
900    indexes=AcquireCacheViewIndexes(image_view);
901    reconstruct_indexes=AcquireCacheViewIndexes(reconstruct_view);
902    for (x=0; x < (long) image->columns; x++)
903    {
904      distance=fabs(p->red-(double) q->red);
905      mean_error_per_pixel+=distance;
906      mean_error+=distance*distance;
907      if (distance > maximum_error)
908        maximum_error=distance;
909      area++;
910      distance=fabs(p->green-(double) q->green);
911      mean_error_per_pixel+=distance;
912      mean_error+=distance*distance;
913      if (distance > maximum_error)
914        maximum_error=distance;
915      area++;
916      distance=fabs(p->blue-(double) q->blue);
917      mean_error_per_pixel+=distance;
918      mean_error+=distance*distance;
919      if (distance > maximum_error)
920        maximum_error=distance;
921      area++;
922      distance=fabs(p->opacity-(double) q->opacity);
923      mean_error_per_pixel+=distance;
924      mean_error+=distance*distance;
925      if (distance > maximum_error)
926        maximum_error=distance;
927      area++;
928      if ((image->colorspace == CMYKColorspace) &&
929          (reconstruct_image->colorspace == CMYKColorspace))
930        {
931          distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
932          mean_error_per_pixel+=distance;
933          mean_error+=distance*distance;
934          if (distance > maximum_error)
935            maximum_error=distance;
936          area++;
937        }
938      p++;
939      q++;
940    }
941  }
942  reconstruct_view=CloseCacheView(reconstruct_view);
943  image_view=CloseCacheView(image_view);
944  image->error.mean_error_per_pixel=(double) (mean_error_per_pixel/area);
945  image->error.normalized_mean_error=(double) (QuantumScale*QuantumScale*
946    mean_error/area);
947  image->error.normalized_maximum_error=(double) (QuantumScale*maximum_error);
948  status=image->error.mean_error_per_pixel == 0.0 ? MagickTrue : MagickFalse;
949  return(status);
950}
Note: See TracBrowser for help on using the browser.