43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/channel.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/compare.h"
54#include "MagickCore/compare-private.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/fourier.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/registry.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/statistic.h"
75#include "MagickCore/statistic-private.h"
76#include "MagickCore/string-private.h"
77#include "MagickCore/thread-private.h"
78#include "MagickCore/threshold.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/utility.h"
81#include "MagickCore/version.h"
115MagickExport Image *CompareImages(Image *image,
const Image *reconstruct_image,
116 const MetricType metric,
double *distortion,ExceptionInfo *exception)
149 assert(image != (Image *) NULL);
150 assert(image->signature == MagickCoreSignature);
151 assert(reconstruct_image != (
const Image *) NULL);
152 assert(reconstruct_image->signature == MagickCoreSignature);
153 assert(distortion != (
double *) NULL);
154 if (IsEventLogging() != MagickFalse)
155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
157 status=GetImageDistortion(image,reconstruct_image,metric,distortion,
159 if (status == MagickFalse)
160 return((Image *) NULL);
161 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
162 SetGeometry(image,&geometry);
163 geometry.width=columns;
164 geometry.height=rows;
165 clone_image=CloneImage(image,0,0,MagickTrue,exception);
166 if (clone_image == (Image *) NULL)
167 return((Image *) NULL);
168 (void) SetImageMask(clone_image,ReadPixelMask,(Image *) NULL,exception);
169 difference_image=ExtentImage(clone_image,&geometry,exception);
170 clone_image=DestroyImage(clone_image);
171 if (difference_image == (Image *) NULL)
172 return((Image *) NULL);
173 (void) ResetImagePage(difference_image,
"0x0+0+0");
174 (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel,exception);
175 highlight_image=CloneImage(image,columns,rows,MagickTrue,exception);
176 if (highlight_image == (Image *) NULL)
178 difference_image=DestroyImage(difference_image);
179 return((Image *) NULL);
181 status=SetImageStorageClass(highlight_image,DirectClass,exception);
182 if (status == MagickFalse)
184 difference_image=DestroyImage(difference_image);
185 highlight_image=DestroyImage(highlight_image);
186 return((Image *) NULL);
188 (void) SetImageMask(highlight_image,ReadPixelMask,(Image *) NULL,exception);
189 (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel,exception);
190 (void) QueryColorCompliance(
"#f1001ecc",AllCompliance,&highlight,exception);
191 artifact=GetImageArtifact(image,
"compare:highlight-color");
192 if (artifact != (
const char *) NULL)
193 (void) QueryColorCompliance(artifact,AllCompliance,&highlight,exception);
194 (void) QueryColorCompliance(
"#ffffffcc",AllCompliance,&lowlight,exception);
195 artifact=GetImageArtifact(image,
"compare:lowlight-color");
196 if (artifact != (
const char *) NULL)
197 (void) QueryColorCompliance(artifact,AllCompliance,&lowlight,exception);
198 (void) QueryColorCompliance(
"#888888cc",AllCompliance,&masklight,exception);
199 artifact=GetImageArtifact(image,
"compare:masklight-color");
200 if (artifact != (
const char *) NULL)
201 (void) QueryColorCompliance(artifact,AllCompliance,&masklight,exception);
205 image_view=AcquireVirtualCacheView(image,exception);
206 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
207 highlight_view=AcquireAuthenticCacheView(highlight_image,exception);
208#if defined(MAGICKCORE_OPENMP_SUPPORT)
209 #pragma omp parallel for schedule(static) shared(status) \
210 magick_number_threads(image,highlight_image,rows,1)
212 for (y=0; y < (ssize_t) rows; y++)
227 if (status == MagickFalse)
229 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
230 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
231 r=QueueCacheViewAuthenticPixels(highlight_view,0,y,columns,1,exception);
232 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL) ||
233 (r == (Quantum *) NULL))
238 for (x=0; x < (ssize_t) columns; x++)
240 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
241 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
243 SetPixelViaPixelInfo(highlight_image,&masklight,r);
244 p+=(ptrdiff_t) GetPixelChannels(image);
245 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
246 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
249 if (IsFuzzyEquivalencePixel(image,p,reconstruct_image,q) == MagickFalse)
250 SetPixelViaPixelInfo(highlight_image,&highlight,r);
252 SetPixelViaPixelInfo(highlight_image,&lowlight,r);
253 p+=(ptrdiff_t) GetPixelChannels(image);
254 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
255 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
257 sync=SyncCacheViewAuthenticPixels(highlight_view,exception);
258 if (sync == MagickFalse)
261 highlight_view=DestroyCacheView(highlight_view);
262 reconstruct_view=DestroyCacheView(reconstruct_view);
263 image_view=DestroyCacheView(image_view);
264 if ((status != MagickFalse) && (difference_image != (Image *) NULL))
265 status=CompositeImage(difference_image,highlight_image,image->compose,
266 MagickTrue,0,0,exception);
267 highlight_image=DestroyImage(highlight_image);
268 if (status == MagickFalse)
269 difference_image=DestroyImage(difference_image);
270 return(difference_image);
307static MagickBooleanType GetAESimilarity(
const Image *image,
308 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
333 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
334 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
335 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
336 image_view=AcquireVirtualCacheView(image,exception);
337 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
338#if defined(MAGICKCORE_OPENMP_SUPPORT)
339 #pragma omp parallel for schedule(static) shared(similarity,status) \
340 magick_number_threads(image,image,rows,1)
342 for (y=0; y < (ssize_t) rows; y++)
349 channel_similarity[MaxPixelChannels+1] = { 0.0 };
354 if (status == MagickFalse)
356 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
357 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
358 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
363 for (x=0; x < (ssize_t) columns; x++)
372 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
373 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
375 p+=(ptrdiff_t) GetPixelChannels(image);
376 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
379 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
380 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
381 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
386 PixelChannel channel = GetPixelChannelChannel(image,i);
387 PixelTrait traits = GetPixelChannelTraits(image,channel);
388 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
390 if (((traits & UpdatePixelTrait) == 0) ||
391 ((reconstruct_traits & UpdatePixelTrait) == 0))
393 if (channel == AlphaPixelChannel)
394 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
397 error=Sa*(double) p[i]-Da*(
double) GetPixelChannel(reconstruct_image,channel,
399 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
401 double ae = fabs(QuantumScale*error);
402 channel_similarity[i]+=ae;
403 channel_similarity[CompositePixelChannel]+=ae;
406 p+=(ptrdiff_t) GetPixelChannels(image);
407 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
409#if defined(MAGICKCORE_OPENMP_SUPPORT)
410 #pragma omp critical (MagickCore_GetAESimilarity)
416 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
418 PixelChannel channel = GetPixelChannelChannel(image,j);
419 PixelTrait traits = GetPixelChannelTraits(image,channel);
420 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
422 if (((traits & UpdatePixelTrait) == 0) ||
423 ((reconstruct_traits & UpdatePixelTrait) == 0))
425 similarity[j]+=channel_similarity[j];
427 similarity[CompositePixelChannel]+=
428 channel_similarity[CompositePixelChannel];
431 reconstruct_view=DestroyCacheView(reconstruct_view);
432 image_view=DestroyCacheView(image_view);
433 area=MagickSafeReciprocal((
double) columns*(
double) rows);
434 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
436 PixelChannel channel = GetPixelChannelChannel(image,k);
437 PixelTrait traits = GetPixelChannelTraits(image,channel);
438 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
440 if (((traits & UpdatePixelTrait) == 0) ||
441 ((reconstruct_traits & UpdatePixelTrait) == 0))
446 similarity[CompositePixelChannel]*=area;
448 similarity[CompositePixelChannel]/=(double) channels;
452static MagickBooleanType GetDPCSimilarity(
const Image *image,
453 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
455#define SimilarityImageTag "Similarity/Image"
463 *reconstruct_statistics;
466 norm[MaxPixelChannels+1] = { 0.0 },
467 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
486 image_statistics=GetImageStatistics(image,exception);
487 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
488 if ((image_statistics == (ChannelStatistics *) NULL) ||
489 (reconstruct_statistics == (ChannelStatistics *) NULL))
491 if (image_statistics != (ChannelStatistics *) NULL)
492 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
494 if (reconstruct_statistics != (ChannelStatistics *) NULL)
495 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
496 reconstruct_statistics);
499 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
500 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
501 image_view=AcquireVirtualCacheView(image,exception);
502 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
503#if defined(MAGICKCORE_OPENMP_SUPPORT)
504 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
505 magick_number_threads(image,image,rows,1)
507 for (y=0; y < (ssize_t) rows; y++)
514 channel_norm[MaxPixelChannels+1] = { 0.0 },
515 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
516 channel_similarity[MaxPixelChannels+1] = { 0.0 };
521 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
522 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
523 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
528 for (x=0; x < (ssize_t) columns; x++)
537 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
538 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
540 p+=(ptrdiff_t) GetPixelChannels(image);
541 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
544 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
545 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
546 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
552 PixelChannel channel = GetPixelChannelChannel(image,i);
553 PixelTrait traits = GetPixelChannelTraits(image,channel);
554 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
556 if (((traits & UpdatePixelTrait) == 0) ||
557 ((reconstruct_traits & UpdatePixelTrait) == 0))
559 if (channel == AlphaPixelChannel)
561 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
562 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
563 channel,q)-reconstruct_statistics[channel].mean);
567 alpha=QuantumScale*(Sa*(double) p[i]-
568 image_statistics[channel].mean);
569 beta=QuantumScale*(Da*(double) GetPixelChannel(reconstruct_image,channel,
570 q)-reconstruct_statistics[channel].mean);
572 channel_similarity[i]+=alpha*beta;
573 channel_norm[i]+=alpha*alpha;
574 channel_reconstruct_norm[i]+=beta*beta;
576 p+=(ptrdiff_t) GetPixelChannels(image);
577 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
579#if defined(MAGICKCORE_OPENMP_SUPPORT)
580 #pragma omp critical (MagickCore_GetDPCSimilarity)
586 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
588 PixelChannel channel = GetPixelChannelChannel(image,j);
589 PixelTrait traits = GetPixelChannelTraits(image,channel);
590 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
592 if (((traits & UpdatePixelTrait) == 0) ||
593 ((reconstruct_traits & UpdatePixelTrait) == 0))
595 similarity[j]+=channel_similarity[j];
596 similarity[CompositePixelChannel]+=channel_similarity[j];
597 norm[j]+=channel_norm[j];
598 norm[CompositePixelChannel]+=channel_norm[j];
599 reconstruct_norm[j]+=channel_reconstruct_norm[j];
600 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
603 if (image->progress_monitor != (MagickProgressMonitor) NULL)
608#if defined(MAGICKCORE_OPENMP_SUPPORT)
612 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
613 if (proceed == MagickFalse)
620 reconstruct_view=DestroyCacheView(reconstruct_view);
621 image_view=DestroyCacheView(image_view);
625 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
627 PixelChannel channel = GetPixelChannelChannel(image,k);
628 PixelTrait traits = GetPixelChannelTraits(image,channel);
629 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
631 if (((traits & UpdatePixelTrait) == 0) ||
632 ((reconstruct_traits & UpdatePixelTrait) == 0))
634 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
636 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
637 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
641 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
642 reconstruct_statistics);
643 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
648static MagickBooleanType GetFUZZSimilarity(
const Image *image,
649 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
673 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
674 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
675 image_view=AcquireVirtualCacheView(image,exception);
676 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
677#if defined(MAGICKCORE_OPENMP_SUPPORT)
678 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
679 magick_number_threads(image,image,rows,1)
681 for (y=0; y < (ssize_t) rows; y++)
689 channel_similarity[MaxPixelChannels+1] = { 0.0 };
694 if (status == MagickFalse)
696 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
697 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
698 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
703 for (x=0; x < (ssize_t) columns; x++)
712 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
713 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
715 p+=(ptrdiff_t) GetPixelChannels(image);
716 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
719 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
720 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
721 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
726 PixelChannel channel = GetPixelChannelChannel(image,i);
727 PixelTrait traits = GetPixelChannelTraits(image,channel);
728 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
730 if (((traits & UpdatePixelTrait) == 0) ||
731 ((reconstruct_traits & UpdatePixelTrait) == 0))
733 if (channel == AlphaPixelChannel)
734 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
737 error=Sa*(double) p[i]-Da*(
double) GetPixelChannel(reconstruct_image,
739 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
741 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
742 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
747 p+=(ptrdiff_t) GetPixelChannels(image);
748 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
750#if defined(MAGICKCORE_OPENMP_SUPPORT)
751 #pragma omp critical (MagickCore_GetFUZZSimilarity)
758 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
760 PixelChannel channel = GetPixelChannelChannel(image,j);
761 PixelTrait traits = GetPixelChannelTraits(image,channel);
762 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
764 if (((traits & UpdatePixelTrait) == 0) ||
765 ((reconstruct_traits & UpdatePixelTrait) == 0))
767 similarity[j]+=channel_similarity[j];
769 similarity[CompositePixelChannel]+=
770 channel_similarity[CompositePixelChannel];
773 reconstruct_view=DestroyCacheView(reconstruct_view);
774 image_view=DestroyCacheView(image_view);
775 area=MagickSafeReciprocal(area);
776 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
778 PixelChannel channel = GetPixelChannelChannel(image,k);
779 PixelTrait traits = GetPixelChannelTraits(image,channel);
780 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
782 if (((traits & UpdatePixelTrait) == 0) ||
783 ((reconstruct_traits & UpdatePixelTrait) == 0))
787 similarity[CompositePixelChannel]*=area;
791static MagickBooleanType GetMAESimilarity(
const Image *image,
792 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
816 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
817 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
818 image_view=AcquireVirtualCacheView(image,exception);
819 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
820#if defined(MAGICKCORE_OPENMP_SUPPORT)
821 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
822 magick_number_threads(image,image,rows,1)
824 for (y=0; y < (ssize_t) rows; y++)
832 channel_similarity[MaxPixelChannels+1] = { 0.0 };
837 if (status == MagickFalse)
839 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
840 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
841 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
846 for (x=0; x < (ssize_t) columns; x++)
855 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
856 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
858 p+=(ptrdiff_t) GetPixelChannels(image);
859 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
862 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
863 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
864 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
869 PixelChannel channel = GetPixelChannelChannel(image,i);
870 PixelTrait traits = GetPixelChannelTraits(image,channel);
871 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
873 if (((traits & UpdatePixelTrait) == 0) ||
874 ((reconstruct_traits & UpdatePixelTrait) == 0))
876 if (channel == AlphaPixelChannel)
877 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
878 reconstruct_image,channel,q));
880 error=QuantumScale*fabs(Sa*(
double) p[i]-Da*(
double)
881 GetPixelChannel(reconstruct_image,channel,q));
882 channel_similarity[i]+=error;
883 channel_similarity[CompositePixelChannel]+=error;
886 p+=(ptrdiff_t) GetPixelChannels(image);
887 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
889#if defined(MAGICKCORE_OPENMP_SUPPORT)
890 #pragma omp critical (MagickCore_GetMAESimilarity)
897 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
899 PixelChannel channel = GetPixelChannelChannel(image,j);
900 PixelTrait traits = GetPixelChannelTraits(image,channel);
901 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
903 if (((traits & UpdatePixelTrait) == 0) ||
904 ((reconstruct_traits & UpdatePixelTrait) == 0))
906 similarity[j]+=channel_similarity[j];
908 similarity[CompositePixelChannel]+=
909 channel_similarity[CompositePixelChannel];
912 reconstruct_view=DestroyCacheView(reconstruct_view);
913 image_view=DestroyCacheView(image_view);
914 area=MagickSafeReciprocal(area);
915 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
917 PixelChannel channel = GetPixelChannelChannel(image,k);
918 PixelTrait traits = GetPixelChannelTraits(image,channel);
919 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
921 if (((traits & UpdatePixelTrait) == 0) ||
922 ((reconstruct_traits & UpdatePixelTrait) == 0))
927 similarity[CompositePixelChannel]*=area;
929 similarity[CompositePixelChannel]/=(double) channels;
933static MagickBooleanType GetMEPPSimilarity(Image *image,
934 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
942 maximum_error = -MagickMaximumValue,
960 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
961 image_view=AcquireVirtualCacheView(image,exception);
962 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
963#if defined(MAGICKCORE_OPENMP_SUPPORT)
964 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
965 magick_number_threads(image,image,rows,1)
967 for (y=0; y < (ssize_t) rows; y++)
975 channel_similarity[MaxPixelChannels+1] = { 0.0 },
976 channel_maximum_error = maximum_error,
977 channel_mean_error = 0.0;
982 if (status == MagickFalse)
984 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
985 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
986 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
991 for (x=0; x < (ssize_t) columns; x++)
1000 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1001 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1003 p+=(ptrdiff_t) GetPixelChannels(image);
1004 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1007 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1008 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1009 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1014 PixelChannel channel = GetPixelChannelChannel(image,i);
1015 PixelTrait traits = GetPixelChannelTraits(image,channel);
1016 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1018 if (((traits & UpdatePixelTrait) == 0) ||
1019 ((reconstruct_traits & UpdatePixelTrait) == 0))
1021 if (channel == AlphaPixelChannel)
1022 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
1023 reconstruct_image,channel,q));
1025 error=QuantumScale*fabs(Sa*(
double) p[i]-Da*(
double)
1026 GetPixelChannel(reconstruct_image,channel,q));
1027 channel_similarity[i]+=error;
1028 channel_similarity[CompositePixelChannel]+=error;
1029 channel_mean_error+=error*error;
1030 if (error > channel_maximum_error)
1031 channel_maximum_error=error;
1034 p+=(ptrdiff_t) GetPixelChannels(image);
1035 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1037#if defined(MAGICKCORE_OPENMP_SUPPORT)
1038 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1045 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1047 PixelChannel channel = GetPixelChannelChannel(image,j);
1048 PixelTrait traits = GetPixelChannelTraits(image,channel);
1049 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1051 if (((traits & UpdatePixelTrait) == 0) ||
1052 ((reconstruct_traits & UpdatePixelTrait) == 0))
1054 similarity[j]+=channel_similarity[j];
1056 similarity[CompositePixelChannel]+=
1057 channel_similarity[CompositePixelChannel];
1058 mean_error+=channel_mean_error;
1059 if (channel_maximum_error > maximum_error)
1060 maximum_error=channel_maximum_error;
1063 reconstruct_view=DestroyCacheView(reconstruct_view);
1064 image_view=DestroyCacheView(image_view);
1065 area=MagickSafeReciprocal(area);
1066 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1068 PixelChannel channel = GetPixelChannelChannel(image,k);
1069 PixelTrait traits = GetPixelChannelTraits(image,channel);
1070 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1072 if (((traits & UpdatePixelTrait) == 0) ||
1073 ((reconstruct_traits & UpdatePixelTrait) == 0))
1075 similarity[k]*=area;
1078 similarity[CompositePixelChannel]*=area;
1080 similarity[CompositePixelChannel]/=(double) channels;
1081 image->error.mean_error_per_pixel=(double) QuantumRange*
1082 similarity[CompositePixelChannel];
1083 image->error.normalized_mean_error=mean_error*area;
1084 image->error.normalized_maximum_error=maximum_error;
1088static MagickBooleanType GetMSESimilarity(
const Image *image,
1089 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1099 status = MagickTrue;
1113 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1114 image_view=AcquireVirtualCacheView(image,exception);
1115 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1116#if defined(MAGICKCORE_OPENMP_SUPPORT)
1117 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1118 magick_number_threads(image,image,rows,1)
1120 for (y=0; y < (ssize_t) rows; y++)
1128 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1133 if (status == MagickFalse)
1135 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1136 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1137 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1142 for (x=0; x < (ssize_t) columns; x++)
1151 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1152 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1154 p+=(ptrdiff_t) GetPixelChannels(image);
1155 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1158 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1159 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1160 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1165 PixelChannel channel = GetPixelChannelChannel(image,i);
1166 PixelTrait traits = GetPixelChannelTraits(image,channel);
1167 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1169 if (((traits & UpdatePixelTrait) == 0) ||
1170 ((reconstruct_traits & UpdatePixelTrait) == 0))
1172 if (channel == AlphaPixelChannel)
1173 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1174 reconstruct_image,channel,q));
1176 error=QuantumScale*(Sa*(double) p[i]-Da*(double) GetPixelChannel(
1177 reconstruct_image,channel,q));
1178 channel_similarity[i]+=error*error;
1179 channel_similarity[CompositePixelChannel]+=error*error;
1182 p+=(ptrdiff_t) GetPixelChannels(image);
1183 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1185#if defined(MAGICKCORE_OPENMP_SUPPORT)
1186 #pragma omp critical (MagickCore_GetMSESimilarity)
1193 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1195 PixelChannel channel = GetPixelChannelChannel(image,j);
1196 PixelTrait traits = GetPixelChannelTraits(image,channel);
1197 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1199 if (((traits & UpdatePixelTrait) == 0) ||
1200 ((reconstruct_traits & UpdatePixelTrait) == 0))
1202 similarity[j]+=channel_similarity[j];
1204 similarity[CompositePixelChannel]+=
1205 channel_similarity[CompositePixelChannel];
1208 reconstruct_view=DestroyCacheView(reconstruct_view);
1209 image_view=DestroyCacheView(image_view);
1210 area=MagickSafeReciprocal(area);
1211 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1213 PixelChannel channel = GetPixelChannelChannel(image,k);
1214 PixelTrait traits = GetPixelChannelTraits(image,channel);
1215 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1217 if (((traits & UpdatePixelTrait) == 0) ||
1218 ((reconstruct_traits & UpdatePixelTrait) == 0))
1220 similarity[k]*=area;
1223 similarity[CompositePixelChannel]*=area;
1225 similarity[CompositePixelChannel]/=(double) channels;
1229static MagickBooleanType GetNCCSimilarity(
const Image *image,
1230 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1238 *reconstruct_statistics;
1241 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1242 variance[MaxPixelChannels+1] = { 0.0 };
1245 status = MagickTrue;
1261 image_statistics=GetImageStatistics(image,exception);
1262 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1263 if ((image_statistics == (ChannelStatistics *) NULL) ||
1264 (reconstruct_statistics == (ChannelStatistics *) NULL))
1266 if (image_statistics != (ChannelStatistics *) NULL)
1267 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1269 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1270 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1271 reconstruct_statistics);
1272 return(MagickFalse);
1274 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1275 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1276 image_view=AcquireVirtualCacheView(image,exception);
1277 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1278#if defined(MAGICKCORE_OPENMP_SUPPORT)
1279 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1280 magick_number_threads(image,image,rows,1)
1282 for (y=0; y < (ssize_t) rows; y++)
1289 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1290 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1291 channel_variance[MaxPixelChannels+1] = { 0.0 };
1296 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1297 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1298 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1303 for (x=0; x < (ssize_t) columns; x++)
1312 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1313 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1315 p+=(ptrdiff_t) GetPixelChannels(image);
1316 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1319 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1320 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1321 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1327 PixelChannel channel = GetPixelChannelChannel(image,i);
1328 PixelTrait traits = GetPixelChannelTraits(image,channel);
1329 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1331 if (((traits & UpdatePixelTrait) == 0) ||
1332 ((reconstruct_traits & UpdatePixelTrait) == 0))
1334 if (channel == AlphaPixelChannel)
1336 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1337 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1338 channel,q)-reconstruct_statistics[channel].mean);
1342 alpha=QuantumScale*(Sa*(double) p[i]-
1343 image_statistics[channel].mean);
1344 beta=QuantumScale*(Da*(double) GetPixelChannel(reconstruct_image,channel,
1345 q)-reconstruct_statistics[channel].mean);
1347 channel_similarity[i]+=alpha*beta;
1348 channel_variance[i]+=alpha*alpha;
1349 channel_reconstruct_variance[i]+=beta*beta;
1351 p+=(ptrdiff_t) GetPixelChannels(image);
1352 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1354#if defined(MAGICKCORE_OPENMP_SUPPORT)
1355 #pragma omp critical (MagickCore_GetNCCSimilarity)
1361 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1363 PixelChannel channel = GetPixelChannelChannel(image,j);
1364 PixelTrait traits = GetPixelChannelTraits(image,channel);
1365 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1367 if (((traits & UpdatePixelTrait) == 0) ||
1368 ((reconstruct_traits & UpdatePixelTrait) == 0))
1370 similarity[j]+=channel_similarity[j];
1371 similarity[CompositePixelChannel]+=channel_similarity[j];
1372 variance[j]+=channel_variance[j];
1373 variance[CompositePixelChannel]+=channel_variance[j];
1374 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1375 reconstruct_variance[CompositePixelChannel]+=
1376 channel_reconstruct_variance[j];
1379 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1384#if defined(MAGICKCORE_OPENMP_SUPPORT)
1388 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1389 if (proceed == MagickFalse)
1396 reconstruct_view=DestroyCacheView(reconstruct_view);
1397 image_view=DestroyCacheView(image_view);
1401 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1403 PixelChannel channel = GetPixelChannelChannel(image,k);
1404 PixelTrait traits = GetPixelChannelTraits(image,channel);
1405 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1407 if (((traits & UpdatePixelTrait) == 0) ||
1408 ((reconstruct_traits & UpdatePixelTrait) == 0))
1410 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1411 sqrt(reconstruct_variance[k]));
1413 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1414 variance[CompositePixelChannel])*sqrt(
1415 reconstruct_variance[CompositePixelChannel]));
1419 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1420 reconstruct_statistics);
1421 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1426static MagickBooleanType GetPASimilarity(
const Image *image,
1427 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1434 status = MagickTrue;
1446 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1447 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1448 image_view=AcquireVirtualCacheView(image,exception);
1449 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1450#if defined(MAGICKCORE_OPENMP_SUPPORT)
1451 #pragma omp parallel for schedule(static) shared(similarity,status) \
1452 magick_number_threads(image,image,rows,1)
1454 for (y=0; y < (ssize_t) rows; y++)
1461 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1466 if (status == MagickFalse)
1468 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1469 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1470 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1475 for (x=0; x < (ssize_t) columns; x++)
1484 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1485 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1487 p+=(ptrdiff_t) GetPixelChannels(image);
1488 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1491 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1492 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1493 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1498 PixelChannel channel = GetPixelChannelChannel(image,i);
1499 PixelTrait traits = GetPixelChannelTraits(image,channel);
1500 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1502 if (((traits & UpdatePixelTrait) == 0) ||
1503 ((reconstruct_traits & UpdatePixelTrait) == 0))
1505 if (channel == AlphaPixelChannel)
1506 distance=QuantumScale*fabs((
double) p[i]-(
double)
1507 GetPixelChannel(reconstruct_image,channel,q));
1509 distance=QuantumScale*fabs(Sa*(
double) p[i]-Da*(
double) GetPixelChannel(
1510 reconstruct_image,channel,q));
1511 if (distance > channel_similarity[i])
1512 channel_similarity[i]=distance;
1513 if (distance > channel_similarity[CompositePixelChannel])
1514 channel_similarity[CompositePixelChannel]=distance;
1516 p+=(ptrdiff_t) GetPixelChannels(image);
1517 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1519#if defined(MAGICKCORE_OPENMP_SUPPORT)
1520 #pragma omp critical (MagickCore_GetPASimilarity)
1526 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1528 PixelChannel channel = GetPixelChannelChannel(image,j);
1529 PixelTrait traits = GetPixelChannelTraits(image,channel);
1530 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1532 if (((traits & UpdatePixelTrait) == 0) ||
1533 ((reconstruct_traits & UpdatePixelTrait) == 0))
1535 if (channel_similarity[j] > similarity[j])
1536 similarity[j]=channel_similarity[j];
1538 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1539 similarity[CompositePixelChannel]=
1540 channel_similarity[CompositePixelChannel];
1543 reconstruct_view=DestroyCacheView(reconstruct_view);
1544 image_view=DestroyCacheView(image_view);
1548static MagickBooleanType GetPDCSimilarity(
const Image *image,
1549 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1559 status = MagickTrue;
1571 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1572 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1573 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1574 image_view=AcquireVirtualCacheView(image,exception);
1575 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1576#if defined(MAGICKCORE_OPENMP_SUPPORT)
1577 #pragma omp parallel for schedule(static) shared(similarity,status) \
1578 magick_number_threads(image,image,rows,1)
1580 for (y=0; y < (ssize_t) rows; y++)
1587 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1592 if (status == MagickFalse)
1594 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1595 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1596 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1601 for (x=0; x < (ssize_t) columns; x++)
1613 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1614 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1616 p+=(ptrdiff_t) GetPixelChannels(image);
1617 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1620 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1621 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1622 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1627 PixelChannel channel = GetPixelChannelChannel(image,i);
1628 PixelTrait traits = GetPixelChannelTraits(image,channel);
1629 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1631 if (((traits & UpdatePixelTrait) == 0) ||
1632 ((reconstruct_traits & UpdatePixelTrait) == 0))
1634 if (channel == AlphaPixelChannel)
1635 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1638 error=Sa*(double) p[i]-Da*(
double) GetPixelChannel(reconstruct_image,
1640 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1642 channel_similarity[i]++;
1647 channel_similarity[CompositePixelChannel]++;
1648 p+=(ptrdiff_t) GetPixelChannels(image);
1649 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1651#if defined(MAGICKCORE_OPENMP_SUPPORT)
1652 #pragma omp critical (MagickCore_GetPDCSimilarity)
1658 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1660 PixelChannel channel = GetPixelChannelChannel(image,j);
1661 PixelTrait traits = GetPixelChannelTraits(image,channel);
1662 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1664 if (((traits & UpdatePixelTrait) == 0) ||
1665 ((reconstruct_traits & UpdatePixelTrait) == 0))
1667 similarity[j]+=channel_similarity[j];
1669 similarity[CompositePixelChannel]+=
1670 channel_similarity[CompositePixelChannel];
1673 reconstruct_view=DestroyCacheView(reconstruct_view);
1674 image_view=DestroyCacheView(image_view);
1678static Image *GetEdgeCorrelationSurface(
const Image *image,
1679 ExceptionInfo *exception)
1690 kernel=AcquireKernelInfo(
"LoG:0,1",exception);
1691 if (kernel == (KernelInfo *) NULL)
1692 return((Image *) NULL);
1693 surface=MorphologyImage(image,ConvolveMorphology,1,kernel,exception);
1694 kernel=DestroyKernelInfo(kernel);
1698static MagickBooleanType GetPHASESimilarity(
const Image *image,
1699 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1702 *edge_reconstruct_view,
1709 correlation[MaxPixelChannels+1] = { 0.0 },
1710 image_sum[MaxPixelChannels+1] = { 0.0 },
1711 image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1712 reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1713 reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1720 status = MagickTrue;
1735 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1736 edge_image=GetEdgeCorrelationSurface(image,exception);
1737 edge_reconstruct=GetEdgeCorrelationSurface(reconstruct_image,exception);
1738 if ((edge_image == (Image *) NULL) || (edge_reconstruct == (Image *) NULL))
1740 if (edge_image != (Image *) NULL)
1741 edge_image=DestroyImage(edge_image);
1742 if (edge_reconstruct != (Image *) NULL)
1743 edge_reconstruct=DestroyImage(edge_reconstruct);
1744 return(MagickFalse);
1746 image_view=AcquireVirtualCacheView(image,exception);
1747 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1748 edge_view=AcquireVirtualCacheView(edge_image,exception);
1749 edge_reconstruct_view=AcquireVirtualCacheView(edge_reconstruct,exception);
1750#if defined(MAGICKCORE_OPENMP_SUPPORT)
1751 #pragma omp parallel for schedule(static) shared(status) \
1752 magick_number_threads(edge_image,edge_reconstruct,rows,1)
1754 for (y=0; y < (ssize_t) rows; y++)
1758 *magick_restrict pm,
1760 *magick_restrict qm;
1764 channel_correlation[MaxPixelChannels+1] = { 0.0 },
1765 channel_image_sum[MaxPixelChannels+1] = { 0.0 },
1766 channel_image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1767 channel_reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1768 channel_reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1774 if (status == MagickFalse)
1776 pm=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1777 qm=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1778 p=GetCacheViewVirtualPixels(edge_view,0,y,columns,1,exception);
1779 q=GetCacheViewVirtualPixels(edge_reconstruct_view,0,y,columns,1,exception);
1780 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL) ||
1781 (pm == (
const Quantum *) NULL) || (qm == (
const Quantum *) NULL))
1786 for (x=0; x < (ssize_t) columns; x++)
1788 if ((GetPixelReadMask(image,pm) <= (QuantumRange/2)) ||
1789 (GetPixelReadMask(reconstruct_image,qm) <= (QuantumRange/2)))
1791 p+=(ptrdiff_t) GetPixelChannels(edge_image);
1792 q+=(ptrdiff_t) GetPixelChannels(edge_reconstruct);
1793 pm+=(ptrdiff_t) GetPixelChannels(image);
1794 qm+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1797 for (i=0; i < (ssize_t) GetPixelChannels(edge_image); i++)
1813 channel=GetPixelChannelChannel(edge_image,i);
1814 traits=GetPixelChannelTraits(edge_image,channel);
1815 reconstruct_traits=GetPixelChannelTraits(edge_reconstruct,channel);
1816 if (((traits & UpdatePixelTrait) == 0) ||
1817 ((reconstruct_traits & UpdatePixelTrait) == 0))
1819 offset=GetPixelChannelOffset(edge_reconstruct,channel);
1822 alpha=QuantumScale*(double) p[i];
1823 beta=QuantumScale*(double) q[offset];
1824 channel_image_sum[i]+=alpha;
1825 channel_image_sum_squared[i]+=alpha*alpha;
1826 channel_reconstruct_sum[i]+=beta;
1827 channel_reconstruct_sum_squared[i]+=beta*beta;
1828 channel_correlation[i]+=alpha*beta;
1831 p+=(ptrdiff_t) GetPixelChannels(edge_image);
1832 q+=(ptrdiff_t) GetPixelChannels(edge_reconstruct);
1833 pm+=(ptrdiff_t) GetPixelChannels(image);
1834 qm+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1836#if defined(MAGICKCORE_OPENMP_SUPPORT)
1837 #pragma omp critical (MagickCore_GetPHASESimilarity)
1841 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1843 correlation[i]+=channel_correlation[i];
1844 image_sum[i]+=channel_image_sum[i];
1845 image_sum_squared[i]+=channel_image_sum_squared[i];
1846 reconstruct_sum[i]+=channel_reconstruct_sum[i];
1847 reconstruct_sum_squared[i]+=channel_reconstruct_sum_squared[i];
1851 edge_reconstruct_view=DestroyCacheView(edge_reconstruct_view);
1852 edge_view=DestroyCacheView(edge_view);
1853 reconstruct_view=DestroyCacheView(reconstruct_view);
1854 image_view=DestroyCacheView(image_view);
1855 edge_image=DestroyImage(edge_image);
1856 edge_reconstruct=DestroyImage(edge_reconstruct);
1857 if (status == MagickFalse)
1858 return(MagickFalse);
1861 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1862 "InsufficientImageDataInRaster",
"`%s'",image->filename);
1863 return(MagickFalse);
1868 similarity[CompositePixelChannel]=0.0;
1869 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1876 reconstruct_variance;
1885 channel=GetPixelChannelChannel(image,j);
1886 traits=GetPixelChannelTraits(image,channel);
1887 reconstruct_traits=GetPixelChannelTraits(reconstruct_image,channel);
1888 if (((traits & UpdatePixelTrait) == 0) ||
1889 ((reconstruct_traits & UpdatePixelTrait) == 0))
1891 numerator=area*correlation[j]-image_sum[j]*reconstruct_sum[j];
1892 image_variance=area*image_sum_squared[j]-image_sum[j]*image_sum[j];
1893 reconstruct_variance=area*reconstruct_sum_squared[j]-reconstruct_sum[j]*
1895 if ((image_variance < MagickEpsilon) &&
1896 (reconstruct_variance < MagickEpsilon))
1897 pearson=(fabs(image_sum[j]-reconstruct_sum[j]) < MagickEpsilon) ?
1901 denominator=sqrt(image_variance)*sqrt(reconstruct_variance);
1902 pearson=denominator < MagickEpsilon ? 0.0 : numerator/denominator;
1908 similarity[j]=pearson;
1909 similarity[CompositePixelChannel]+=pearson;
1913 similarity[CompositePixelChannel]/=(double) channels;
1917static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1918 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1920 ChannelPerceptualHash
1934 channel_phash=GetImagePerceptualHash(image,exception);
1935 if (channel_phash == (ChannelPerceptualHash *) NULL)
1936 return(MagickFalse);
1937 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1938 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1940 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1942 return(MagickFalse);
1944 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1952 PixelChannel channel = GetPixelChannelChannel(image,i);
1953 PixelTrait traits = GetPixelChannelTraits(image,channel);
1954 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1956 if (((traits & UpdatePixelTrait) == 0) ||
1957 ((reconstruct_traits & UpdatePixelTrait) == 0))
1959 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1968 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1973 alpha=channel_phash[i].phash[j][k];
1974 beta=reconstruct_phash[i].phash[j][k];
1976 if (IsNaN(error) != 0)
1978 difference+=error*error;
1981 similarity[i]+=difference;
1982 similarity[CompositePixelChannel]+=difference;
1986 similarity[CompositePixelChannel]/=(double) channels;
1987 artifact=GetImageArtifact(image,
"phash:normalize");
1988 if (IsStringTrue(artifact) != MagickFalse)
1990 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1992 PixelChannel channel = GetPixelChannelChannel(image,i);
1993 PixelTrait traits = GetPixelChannelTraits(image,channel);
1994 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1996 if (((traits & UpdatePixelTrait) == 0) ||
1997 ((reconstruct_traits & UpdatePixelTrait) == 0))
1999 similarity[i]=sqrt(similarity[i]/(
double)
2000 channel_phash[0].number_colorspaces);
2002 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
2003 (
double) channel_phash[0].number_colorspaces);
2008 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
2010 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
2014static MagickBooleanType GetPSNRSimilarity(
const Image *image,
2015 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2018 status = MagickTrue;
2026 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2027 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2029 PixelChannel channel = GetPixelChannelChannel(image,i);
2030 PixelTrait traits = GetPixelChannelTraits(image,channel);
2031 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2033 if (((traits & UpdatePixelTrait) == 0) ||
2034 ((reconstruct_traits & UpdatePixelTrait) == 0))
2036 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
2037 similarity[i]))/MagickSafePSNRRecipicol(10.0);
2039 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
2040 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
2041 MagickSafePSNRRecipicol(10.0);
2045static MagickBooleanType GetRMSESimilarity(
const Image *image,
2046 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2048#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2051 status = MagickTrue;
2059 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2060 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2062 PixelChannel channel = GetPixelChannelChannel(image,i);
2063 PixelTrait traits = GetPixelChannelTraits(image,channel);
2064 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2066 if (((traits & UpdatePixelTrait) == 0) ||
2067 ((reconstruct_traits & UpdatePixelTrait) == 0))
2069 similarity[i]=RMSESquareRoot(similarity[i]);
2071 similarity[CompositePixelChannel]=RMSESquareRoot(
2072 similarity[CompositePixelChannel]);
2076static MagickBooleanType GetSSIMSimularity(
const Image *image,
2077 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2079#define SSIMRadius 5.0
2080#define SSIMSigma 1.5
2090 geometry[MagickPathExtent];
2106 status = MagickTrue;
2121 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2122 if (artifact != (
const char *) NULL)
2123 radius=StringToDouble(artifact,(
char **) NULL);
2125 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2126 if (artifact != (
const char *) NULL)
2127 sigma=StringToDouble(artifact,(
char **) NULL);
2128 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.17gx%.17g",
2130 kernel_info=AcquireKernelInfo(geometry,exception);
2131 if (kernel_info == (KernelInfo *) NULL)
2132 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2134 c1=pow(SSIMK1*SSIML,2.0);
2135 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2136 if (artifact != (
const char *) NULL)
2137 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2138 c2=pow(SSIMK2*SSIML,2.0);
2139 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2140 if (artifact != (
const char *) NULL)
2141 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2142 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2143 image_view=AcquireVirtualCacheView(image,exception);
2144 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2145#if defined(MAGICKCORE_OPENMP_SUPPORT)
2146 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2147 magick_number_threads(image,reconstruct_image,rows,1)
2149 for (y=0; y < (ssize_t) rows; y++)
2157 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2163 if (status == MagickFalse)
2165 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2166 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2167 kernel_info->height,exception);
2168 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2169 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2170 kernel_info->height,exception);
2171 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2176 for (x=0; x < (ssize_t) columns; x++)
2179 *magick_restrict reconstruct,
2180 *magick_restrict test;
2183 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2184 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2185 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2186 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2187 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2195 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2196 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2198 p+=(ptrdiff_t) GetPixelChannels(image);
2199 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2202 k=kernel_info->values;
2205 for (v=0; v < (ssize_t) kernel_info->height; v++)
2210 for (u=0; u < (ssize_t) kernel_info->width; u++)
2212 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2218 PixelChannel channel = GetPixelChannelChannel(image,i);
2219 PixelTrait traits = GetPixelChannelTraits(image,channel);
2220 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2221 reconstruct_image,channel);
2222 if (((traits & UpdatePixelTrait) == 0) ||
2223 ((reconstruct_traits & UpdatePixelTrait) == 0))
2225 x_pixel=QuantumScale*(double) test[i];
2226 x_pixel_mu[i]+=(*k)*x_pixel;
2227 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2228 y_pixel=QuantumScale*(double)
2229 GetPixelChannel(reconstruct_image,channel,reconstruct);
2230 y_pixel_mu[i]+=(*k)*y_pixel;
2231 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2232 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2235 test+=(ptrdiff_t) GetPixelChannels(image);
2236 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2238 test+=(ptrdiff_t) ((
double) GetPixelChannels(image)*(
double) columns);
2239 reconstruct+=(ptrdiff_t) ((
double) GetPixelChannels(reconstruct_image)*
2242 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2247 x_pixel_sigmas_squared,
2251 y_pixel_sigmas_squared;
2253 PixelChannel channel = GetPixelChannelChannel(image,i);
2254 PixelTrait traits = GetPixelChannelTraits(image,channel);
2255 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2256 reconstruct_image,channel);
2257 if (((traits & UpdatePixelTrait) == 0) ||
2258 ((reconstruct_traits & UpdatePixelTrait) == 0))
2260 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2261 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2262 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2263 xy_sigmas=xy_sigma[i]-xy_mu;
2264 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2265 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2266 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2267 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2268 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2269 channel_similarity[i]+=ssim;
2270 channel_similarity[CompositePixelChannel]+=ssim;
2272 p+=(ptrdiff_t) GetPixelChannels(image);
2273 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2276#if defined(MAGICKCORE_OPENMP_SUPPORT)
2277 #pragma omp critical (MagickCore_GetSSIMSimularity)
2284 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2286 PixelChannel channel = GetPixelChannelChannel(image,j);
2287 PixelTrait traits = GetPixelChannelTraits(image,channel);
2288 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2290 if (((traits & UpdatePixelTrait) == 0) ||
2291 ((reconstruct_traits & UpdatePixelTrait) == 0))
2293 similarity[j]+=channel_similarity[j];
2295 similarity[CompositePixelChannel]+=
2296 channel_similarity[CompositePixelChannel];
2299 image_view=DestroyCacheView(image_view);
2300 reconstruct_view=DestroyCacheView(reconstruct_view);
2301 kernel_info=DestroyKernelInfo(kernel_info);
2302 area=MagickSafeReciprocal(area);
2303 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2305 PixelChannel channel = GetPixelChannelChannel(image,l);
2306 PixelTrait traits = GetPixelChannelTraits(image,channel);
2307 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2309 if (((traits & UpdatePixelTrait) == 0) ||
2310 ((reconstruct_traits & UpdatePixelTrait) == 0))
2312 similarity[l]*=area;
2315 similarity[CompositePixelChannel]*=area;
2317 similarity[CompositePixelChannel]/=(double) channels;
2321static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2322 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2325 status = MagickTrue;
2333 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2334 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2336 PixelChannel channel = GetPixelChannelChannel(image,i);
2337 PixelTrait traits = GetPixelChannelTraits(image,channel);
2338 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2340 if (((traits & UpdatePixelTrait) == 0) ||
2341 ((reconstruct_traits & UpdatePixelTrait) == 0))
2343 similarity[i]=(1.0-similarity[i])/2.0;
2345 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2349MagickExport MagickBooleanType GetImageDistortion(Image *image,
2350 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2351 ExceptionInfo *exception)
2353#define CompareMetricNotSupportedException "metric not supported"
2356 *channel_similarity;
2359 status = MagickTrue;
2364 assert(image != (Image *) NULL);
2365 assert(image->signature == MagickCoreSignature);
2366 assert(reconstruct_image != (
const Image *) NULL);
2367 assert(reconstruct_image->signature == MagickCoreSignature);
2368 assert(distortion != (
double *) NULL);
2369 if (IsEventLogging() != MagickFalse)
2370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2375 length=MaxPixelChannels+1UL;
2376 channel_similarity=(
double *) AcquireQuantumMemory(length,
2377 sizeof(*channel_similarity));
2378 if (channel_similarity == (
double *) NULL)
2379 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2380 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2383 case AbsoluteErrorMetric:
2385 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2389 case DotProductCorrelationErrorMetric:
2391 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2395 case FuzzErrorMetric:
2397 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2401 case MeanAbsoluteErrorMetric:
2403 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2407 case MeanErrorPerPixelErrorMetric:
2409 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2413 case MeanSquaredErrorMetric:
2415 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2419 case NormalizedCrossCorrelationErrorMetric:
2421 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2425 case PeakAbsoluteErrorMetric:
2427 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2431 case PeakSignalToNoiseRatioErrorMetric:
2433 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2437 case PerceptualHashErrorMetric:
2439 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2443 case PhaseCorrelationErrorMetric:
2445 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2449 case PixelDifferenceCountErrorMetric:
2451 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2455 case RootMeanSquaredErrorMetric:
2456 case UndefinedErrorMetric:
2459 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2463 case StructuralDissimilarityErrorMetric:
2465 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2469 case StructuralSimilarityErrorMetric:
2471 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2476 *distortion=channel_similarity[CompositePixelChannel];
2479 case DotProductCorrelationErrorMetric:
2480 case NormalizedCrossCorrelationErrorMetric:
2481 case PhaseCorrelationErrorMetric:
2482 case StructuralSimilarityErrorMetric:
2484 *distortion=(1.0-(*distortion))/2.0;
2489 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2490 if (fabs(*distortion) < MagickEpsilon)
2492 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2528MagickExport
double *GetImageDistortions(Image *image,
2529 const Image *reconstruct_image,
const MetricType metric,
2530 ExceptionInfo *exception)
2534 *channel_similarity;
2537 status = MagickTrue;
2545 assert(image != (Image *) NULL);
2546 assert(image->signature == MagickCoreSignature);
2547 assert(reconstruct_image != (
const Image *) NULL);
2548 assert(reconstruct_image->signature == MagickCoreSignature);
2549 if (IsEventLogging() != MagickFalse)
2550 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2554 length=MaxPixelChannels+1UL;
2555 channel_similarity=(
double *) AcquireQuantumMemory(length,
2556 sizeof(*channel_similarity));
2557 if (channel_similarity == (
double *) NULL)
2558 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2559 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2562 case AbsoluteErrorMetric:
2564 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2568 case DotProductCorrelationErrorMetric:
2570 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2574 case FuzzErrorMetric:
2576 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2580 case MeanAbsoluteErrorMetric:
2582 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2586 case MeanErrorPerPixelErrorMetric:
2588 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2592 case MeanSquaredErrorMetric:
2594 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2598 case NormalizedCrossCorrelationErrorMetric:
2600 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2604 case PeakAbsoluteErrorMetric:
2606 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2610 case PeakSignalToNoiseRatioErrorMetric:
2612 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2616 case PerceptualHashErrorMetric:
2618 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2622 case PhaseCorrelationErrorMetric:
2624 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2628 case PixelDifferenceCountErrorMetric:
2630 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2634 case RootMeanSquaredErrorMetric:
2635 case UndefinedErrorMetric:
2638 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2642 case StructuralDissimilarityErrorMetric:
2644 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2648 case StructuralSimilarityErrorMetric:
2650 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2655 if (status == MagickFalse)
2657 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2658 return((
double *) NULL);
2660 distortion=channel_similarity;
2663 case DotProductCorrelationErrorMetric:
2664 case NormalizedCrossCorrelationErrorMetric:
2665 case PhaseCorrelationErrorMetric:
2666 case StructuralSimilarityErrorMetric:
2668 for (i=0; i <= MaxPixelChannels; i++)
2669 distortion[i]=(1.0-distortion[i])/2.0;
2674 for (i=0; i <= MaxPixelChannels; i++)
2675 if (fabs(distortion[i]) < MagickEpsilon)
2677 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2678 distortion[CompositePixelChannel]);
2710MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2711 const Image *reconstruct_image,ExceptionInfo *exception)
2724 assert(image != (Image *) NULL);
2725 assert(image->signature == MagickCoreSignature);
2726 assert(reconstruct_image != (
const Image *) NULL);
2727 assert(reconstruct_image->signature == MagickCoreSignature);
2728 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2729 image_view=AcquireVirtualCacheView(image,exception);
2730 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2731 for (y=0; y < (ssize_t) rows; y++)
2740 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2741 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2742 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2744 for (x=0; x < (ssize_t) columns; x++)
2749 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2754 PixelChannel channel = GetPixelChannelChannel(image,i);
2755 PixelTrait traits = GetPixelChannelTraits(image,channel);
2756 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2758 if (((traits & UpdatePixelTrait) == 0) ||
2759 ((reconstruct_traits & UpdatePixelTrait) == 0))
2761 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2763 if (distance >= MagickEpsilon)
2766 if (i < (ssize_t) GetPixelChannels(image))
2768 p+=(ptrdiff_t) GetPixelChannels(image);
2769 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2771 if (x < (ssize_t) columns)
2774 reconstruct_view=DestroyCacheView(reconstruct_view);
2775 image_view=DestroyCacheView(image_view);
2776 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2828MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2829 const Image *reconstruct_image,ExceptionInfo *exception)
2832 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2837 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2839 if (status == MagickFalse)
2840 return(MagickFalse);
2841 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2842 MagickTrue : MagickFalse;
2889#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2890static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2891 const Image *beta_image,ExceptionInfo *exception)
2894 *alpha_fft = (Image *) NULL,
2895 *beta_fft = (Image *) NULL,
2896 *complex_conjugate = (Image *) NULL,
2897 *complex_multiplication = (Image *) NULL,
2898 *cross_correlation = (Image *) NULL,
2899 *temp_image = (Image *) NULL;
2904 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2905 if (temp_image == (Image *) NULL)
2906 return((Image *) NULL);
2907 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2908 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2909 temp_image=DestroyImageList(temp_image);
2910 if (beta_fft == (Image *) NULL)
2911 return((Image *) NULL);
2915 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2916 beta_fft=DestroyImageList(beta_fft);
2917 if (complex_conjugate == (Image *) NULL)
2918 return((Image *) NULL);
2922 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2923 if (temp_image == (Image *) NULL)
2925 complex_conjugate=DestroyImageList(complex_conjugate);
2926 return((Image *) NULL);
2928 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2929 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2930 temp_image=DestroyImageList(temp_image);
2931 if (alpha_fft == (Image *) NULL)
2933 complex_conjugate=DestroyImageList(complex_conjugate);
2934 return((Image *) NULL);
2939 DisableCompositeClampUnlessSpecified(complex_conjugate);
2940 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2941 AppendImageToList(&complex_conjugate,alpha_fft);
2942 complex_multiplication=ComplexImages(complex_conjugate,
2943 MultiplyComplexOperator,exception);
2944 complex_conjugate=DestroyImageList(complex_conjugate);
2945 if (complex_multiplication == (Image *) NULL)
2946 return((Image *) NULL);
2950 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2951 complex_multiplication->next,MagickFalse,exception);
2952 complex_multiplication=DestroyImageList(complex_multiplication);
2953 return(cross_correlation);
2956static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2957 ExceptionInfo *exception)
2965 kernel_info=AcquireKernelInfo(kernel,exception);
2966 if (kernel_info == (KernelInfo *) NULL)
2967 return((Image *) NULL);
2968 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2970 kernel_info=DestroyKernelInfo(kernel_info);
2971 return(derivative_image);
2974static Image *SIMDivideImage(
const Image *numerator_image,
2975 const Image *denominator_image,ExceptionInfo *exception)
2985 status = MagickTrue;
2993 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2994 if (divide_image == (Image *) NULL)
2995 return(divide_image);
2996 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2997 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2998#if defined(MAGICKCORE_OPENMP_SUPPORT)
2999 #pragma omp parallel for schedule(static) shared(status) \
3000 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
3002 for (y=0; y < (ssize_t) divide_image->rows; y++)
3013 if (status == MagickFalse)
3015 p=GetCacheViewVirtualPixels(denominator_view,0,y,
3016 denominator_image->columns,1,exception);
3017 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
3019 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3024 for (x=0; x < (ssize_t) divide_image->columns; x++)
3029 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
3031 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
3032 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
3033 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
3035 if (((traits & UpdatePixelTrait) == 0) ||
3036 ((denominator_traits & UpdatePixelTrait) == 0))
3038 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
3039 (
double) GetPixelChannel(denominator_image,channel,p)));
3041 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
3042 q+=(ptrdiff_t) GetPixelChannels(divide_image);
3044 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
3047 denominator_view=DestroyCacheView(denominator_view);
3048 numerator_view=DestroyCacheView(numerator_view);
3049 if (status == MagickFalse)
3050 divide_image=DestroyImage(divide_image);
3051 return(divide_image);
3054static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3055 const Image *source_image,ExceptionInfo *exception)
3064 divide_image=SIMDivideImage(image,magnitude_image,exception);
3065 if (divide_image == (Image *) NULL)
3066 return((Image *) NULL);
3067 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3068 ÷_image->background_color);
3069 SetGeometry(source_image,&geometry);
3070 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3071 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3072 result_image=ExtentImage(divide_image,&geometry,exception);
3073 divide_image=DestroyImage(divide_image);
3074 return(result_image);
3077static MagickBooleanType SIMFilterImageNaNs(Image *image,
3078 ExceptionInfo *exception)
3084 status = MagickTrue;
3092 image_view=AcquireAuthenticCacheView(image,exception);
3093#if defined(MAGICKCORE_OPENMP_SUPPORT)
3094 #pragma omp parallel for schedule(static) shared(status) \
3095 magick_number_threads(image,image,image->rows,1)
3097 for (y=0; y < (ssize_t) image->rows; y++)
3105 if (status == MagickFalse)
3107 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3108 if (q == (Quantum *) NULL)
3113 for (x=0; x < (ssize_t) image->columns; x++)
3118 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3120 PixelChannel channel = GetPixelChannelChannel(image,i);
3121 PixelTrait traits = GetPixelChannelTraits(image,channel);
3122 if ((traits & UpdatePixelTrait) == 0)
3124 if (IsNaN((
double) q[i]) != 0)
3127 q+=(ptrdiff_t) GetPixelChannels(image);
3129 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3132 image_view=DestroyCacheView(image_view);
3136static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3145 status = MagickTrue;
3153 square_image=CloneImage(image,0,0,MagickTrue,exception);
3154 if (square_image == (Image *) NULL)
3155 return(square_image);
3156 image_view=AcquireAuthenticCacheView(square_image,exception);
3157#if defined(MAGICKCORE_OPENMP_SUPPORT)
3158 #pragma omp parallel for schedule(static) shared(status) \
3159 magick_number_threads(square_image,square_image,square_image->rows,1)
3161 for (y=0; y < (ssize_t) square_image->rows; y++)
3169 if (status == MagickFalse)
3171 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3173 if (q == (Quantum *) NULL)
3178 for (x=0; x < (ssize_t) square_image->columns; x++)
3183 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3185 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3186 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3187 if ((traits & UpdatePixelTrait) == 0)
3189 q[i]=(Quantum) (QuantumScale*(
double) q[i]*(
double) q[i]);
3191 q+=(ptrdiff_t) GetPixelChannels(square_image);
3193 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3196 image_view=DestroyCacheView(image_view);
3197 if (status == MagickFalse)
3198 square_image=DestroyImage(square_image);
3199 return(square_image);
3202static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3203 ExceptionInfo *exception)
3211 status = MagickTrue;
3213 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3214 xsq_image=SIMSquareImage(alpha_image,exception);
3215 if (xsq_image == (Image *) NULL)
3216 return((Image *) NULL);
3217 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3218 ysq_image=SIMSquareImage(beta_image,exception);
3219 if (ysq_image == (Image *) NULL)
3221 xsq_image=DestroyImage(xsq_image);
3222 return((Image *) NULL);
3224 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3226 magnitude_image=xsq_image;
3227 ysq_image=DestroyImage(ysq_image);
3228 if (status == MagickFalse)
3230 magnitude_image=DestroyImage(magnitude_image);
3231 return((Image *) NULL);
3233 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3234 if (status == MagickFalse)
3236 magnitude_image=DestroyImage(magnitude_image);
3237 return (Image *) NULL;
3239 return(magnitude_image);
3242static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3243 RectangleInfo *offset,ExceptionInfo *exception)
3262 status = MagickTrue;
3265 maxima_info = { -MagickMaximumValue, 0, 0 };
3273 image_view=AcquireVirtualCacheView(image,exception);
3274 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3276 if (q != (
const Quantum *) NULL)
3277 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3278#if defined(MAGICKCORE_OPENMP_SUPPORT)
3279 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3280 magick_number_threads(image,image,image->rows,1)
3282 for (y=0; y < (ssize_t) image->rows; y++)
3288 channel_maxima = { -MagickMaximumValue, 0, 0 };
3293 if (status == MagickFalse)
3295 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3296 if (p == (
const Quantum *) NULL)
3301 channel_maxima=maxima_info;
3302 for (x=0; x < (ssize_t) image->columns; x++)
3307 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3312 PixelChannel channel = GetPixelChannelChannel(image,i);
3313 PixelTrait traits = GetPixelChannelTraits(image,channel);
3314 if ((traits & UpdatePixelTrait) == 0)
3316 pixel=(double) p[i];
3317 if (IsNaN(pixel) != 0)
3319 if (pixel > channel_maxima.maxima)
3321 channel_maxima.maxima=(double) p[i];
3326 p+=(ptrdiff_t) GetPixelChannels(image);
3328#if defined(MAGICKCORE_OPENMP_SUPPORT)
3329 #pragma omp critical (MagickCore_SIMMaximaImage)
3331 if (channel_maxima.maxima > maxima_info.maxima)
3332 maxima_info=channel_maxima;
3334 image_view=DestroyCacheView(image_view);
3335 *maxima=maxima_info.maxima;
3336 offset->x=maxima_info.x;
3337 offset->y=maxima_info.y;
3341static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3342 RectangleInfo *offset,ExceptionInfo *exception)
3361 status = MagickTrue;
3364 minima_info = { MagickMaximumValue, 0, 0 };
3372 image_view=AcquireVirtualCacheView(image,exception);
3373 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3375 if (q != (
const Quantum *) NULL)
3376 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3377#if defined(MAGICKCORE_OPENMP_SUPPORT)
3378 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3379 magick_number_threads(image,image,image->rows,1)
3381 for (y=0; y < (ssize_t) image->rows; y++)
3387 channel_minima = { MagickMaximumValue, 0, 0 };
3392 if (status == MagickFalse)
3394 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3395 if (p == (
const Quantum *) NULL)
3400 channel_minima=minima_info;
3401 for (x=0; x < (ssize_t) image->columns; x++)
3406 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3411 PixelChannel channel = GetPixelChannelChannel(image,i);
3412 PixelTrait traits = GetPixelChannelTraits(image,channel);
3413 if ((traits & UpdatePixelTrait) == 0)
3415 pixel=(double) p[i];
3416 if (IsNaN(pixel) != 0)
3418 if (pixel < channel_minima.minima)
3420 channel_minima.minima=pixel;
3425 p+=(ptrdiff_t) GetPixelChannels(image);
3427#if defined(MAGICKCORE_OPENMP_SUPPORT)
3428 #pragma omp critical (MagickCore_SIMMinimaImage)
3430 if (channel_minima.minima < minima_info.minima)
3431 minima_info=channel_minima;
3433 image_view=DestroyCacheView(image_view);
3434 *minima=minima_info.minima;
3435 offset->x=minima_info.x;
3436 offset->y=minima_info.y;
3440static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3441 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3447 status = MagickTrue;
3455 image_view=AcquireAuthenticCacheView(image,exception);
3456#if defined(MAGICKCORE_OPENMP_SUPPORT)
3457 #pragma omp parallel for schedule(static) shared(status) \
3458 magick_number_threads(image,image,image->rows,1)
3460 for (y=0; y < (ssize_t) image->rows; y++)
3468 if (status == MagickFalse)
3470 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3471 if (q == (Quantum *) NULL)
3476 for (x=0; x < (ssize_t) image->columns; x++)
3481 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3483 PixelChannel channel = GetPixelChannelChannel(image,i);
3484 PixelTrait traits = GetPixelChannelTraits(image,channel);
3485 if ((traits & UpdatePixelTrait) == 0)
3487 if (channel_statistics != (
const ChannelStatistics *) NULL)
3488 q[i]=(Quantum) (factor*(
double) q[i]*QuantumScale*
3489 channel_statistics[channel].standard_deviation);
3491 q[i]=(Quantum) (factor*(
double) q[i]);
3493 q+=(ptrdiff_t) GetPixelChannels(image);
3495 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3498 image_view=DestroyCacheView(image_view);
3502static Image *SIMPhaseCorrelationImage(
const Image *target_image,
3503 const Image *reconstruct_image,
const Image *magnitude_image,
3504 ExceptionInfo *exception)
3507 *target_fft = (Image *) NULL,
3508 *reconstruct_fft = (Image *) NULL,
3509 *complex_multiplication = (Image *) NULL,
3510 *cross_correlation = (Image *) NULL;
3515 reconstruct_fft=CloneImage(reconstruct_image,0,0,MagickTrue,exception);
3516 if (reconstruct_fft == NULL)
3517 return((Image *) NULL);
3518 (void) SetImageArtifact(reconstruct_fft,
"fourier:normalize",
"inverse");
3519 reconstruct_fft=ForwardFourierTransformImage(reconstruct_fft,MagickFalse,
3521 if (reconstruct_fft == NULL)
3522 return((Image *) NULL);
3526 target_fft=CloneImage(target_image,0,0,MagickTrue,exception);
3527 if (target_fft == (Image *) NULL)
3529 reconstruct_fft=DestroyImageList(reconstruct_fft);
3530 return((Image *) NULL);
3532 (void) SetImageArtifact(target_fft,
"fourier:normalize",
"inverse");
3533 target_fft=ForwardFourierTransformImage(target_fft,MagickFalse,exception);
3534 if (target_fft == (Image *) NULL)
3536 reconstruct_fft=DestroyImageList(reconstruct_fft);
3537 return((Image *) NULL);
3542 reconstruct_fft=ComplexImages(reconstruct_fft,ConjugateComplexOperator,
3544 if (reconstruct_fft == (Image *) NULL)
3546 target_fft=DestroyImageList(target_fft);
3547 return((Image *) NULL);
3552 AppendImageToList(&reconstruct_fft,target_fft);
3553 DisableCompositeClampUnlessSpecified(reconstruct_fft);
3554 DisableCompositeClampUnlessSpecified(reconstruct_fft->next);
3555 complex_multiplication=ComplexImages(reconstruct_fft,MultiplyComplexOperator,
3557 reconstruct_fft=DestroyImageList(reconstruct_fft);
3558 if (complex_multiplication == (Image *) NULL)
3559 return((Image *) NULL);
3560 if (complex_multiplication->next != (Image *) NULL)
3565 DisableCompositeClampUnlessSpecified(complex_multiplication);
3566 DisableCompositeClampUnlessSpecified(complex_multiplication->next);
3567 (void) CompositeImage(complex_multiplication,magnitude_image,
3568 DivideSrcCompositeOp,MagickTrue,0,0,exception);
3569 (void) CompositeImage(complex_multiplication->next,magnitude_image,
3570 DivideSrcCompositeOp,MagickTrue,0,0,exception);
3575 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3576 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3577 complex_multiplication->next,MagickFalse,exception);
3578 complex_multiplication=DestroyImageList(complex_multiplication);
3579 return(cross_correlation);
3582static MagickBooleanType SIMSetImageMean(Image *image,
3583 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3589 status = MagickTrue;
3597 image_view=AcquireAuthenticCacheView(image,exception);
3598#if defined(MAGICKCORE_OPENMP_SUPPORT)
3599 #pragma omp parallel for schedule(static) shared(status) \
3600 magick_number_threads(image,image,image->rows,1)
3602 for (y=0; y < (ssize_t) image->rows; y++)
3610 if (status == MagickFalse)
3612 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3613 if (q == (Quantum *) NULL)
3618 for (x=0; x < (ssize_t) image->columns; x++)
3623 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3625 PixelChannel channel = GetPixelChannelChannel(image,i);
3626 PixelTrait traits = GetPixelChannelTraits(image,channel);
3627 if ((traits & UpdatePixelTrait) == 0)
3629 q[i]=(Quantum) channel_statistics[channel].mean;
3631 q+=(ptrdiff_t) GetPixelChannels(image);
3633 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3636 image_view=DestroyCacheView(image_view);
3640static Image *SIMSubtractImageMean(
const Image *alpha_image,
3641 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3642 ExceptionInfo *exception)
3652 status = MagickTrue;
3660 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3661 MagickTrue,exception);
3662 if (subtract_image == (Image *) NULL)
3663 return(subtract_image);
3664 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3665 beta_view=AcquireVirtualCacheView(beta_image,exception);
3666#if defined(MAGICKCORE_OPENMP_SUPPORT)
3667 #pragma omp parallel for schedule(static) shared(status) \
3668 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3670 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3681 if (status == MagickFalse)
3683 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3684 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3686 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3691 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3696 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3698 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3699 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3700 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3701 if (((traits & UpdatePixelTrait) == 0) ||
3702 ((beta_traits & UpdatePixelTrait) == 0))
3704 if ((x >= (ssize_t) beta_image->columns) ||
3705 (y >= (ssize_t) beta_image->rows))
3708 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3709 channel_statistics[channel].mean);
3711 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3712 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3714 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3717 beta_view=DestroyCacheView(beta_view);
3718 image_view=DestroyCacheView(image_view);
3719 if (status == MagickFalse)
3720 subtract_image=DestroyImage(subtract_image);
3721 return(subtract_image);
3724static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3725 ExceptionInfo *exception)
3734 status = MagickTrue;
3742 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3743 MagickTrue,exception);
3744 if (unity_image == (Image *) NULL)
3745 return(unity_image);
3746 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3747 return(DestroyImage(unity_image));
3748 image_view=AcquireAuthenticCacheView(unity_image,exception);
3749#if defined(MAGICKCORE_OPENMP_SUPPORT)
3750 #pragma omp parallel for schedule(static) shared(status) \
3751 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3753 for (y=0; y < (ssize_t) unity_image->rows; y++)
3761 if (status == MagickFalse)
3763 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3765 if (q == (Quantum *) NULL)
3770 for (x=0; x < (ssize_t) unity_image->columns; x++)
3775 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3777 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3778 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3779 if ((traits & UpdatePixelTrait) == 0)
3781 if ((x >= (ssize_t) beta_image->columns) ||
3782 (y >= (ssize_t) beta_image->rows))
3787 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3789 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3792 image_view=DestroyCacheView(image_view);
3793 if (status == MagickFalse)
3794 unity_image=DestroyImage(unity_image);
3795 return(unity_image);
3798static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3799 ExceptionInfo *exception)
3809 status = MagickTrue;
3817 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3818 if (variance_image == (Image *) NULL)
3819 return(variance_image);
3820 image_view=AcquireAuthenticCacheView(variance_image,exception);
3821 beta_view=AcquireVirtualCacheView(beta_image,exception);
3822#if defined(MAGICKCORE_OPENMP_SUPPORT)
3823 #pragma omp parallel for schedule(static) shared(status) \
3824 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3826 for (y=0; y < (ssize_t) variance_image->rows; y++)
3837 if (status == MagickFalse)
3839 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3841 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3843 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3848 for (x=0; x < (ssize_t) variance_image->columns; x++)
3853 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3858 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3859 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3860 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3861 if (((traits & UpdatePixelTrait) == 0) ||
3862 ((beta_traits & UpdatePixelTrait) == 0))
3864 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3865 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3866 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3868 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3869 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3871 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3874 beta_view=DestroyCacheView(beta_view);
3875 image_view=DestroyCacheView(image_view);
3876 if (status == MagickFalse)
3877 variance_image=DestroyImage(variance_image);
3878 return(variance_image);
3881static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3882 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3884#define ThrowDPCSimilarityException() \
3886 if (dot_product_image != (Image *) NULL) \
3887 dot_product_image=DestroyImage(dot_product_image); \
3888 if (magnitude_image != (Image *) NULL) \
3889 magnitude_image=DestroyImage(magnitude_image); \
3890 if (reconstruct_image != (Image *) NULL) \
3891 reconstruct_image=DestroyImage(reconstruct_image); \
3892 if (rx_image != (Image *) NULL) \
3893 rx_image=DestroyImage(rx_image); \
3894 if (ry_image != (Image *) NULL) \
3895 ry_image=DestroyImage(ry_image); \
3896 if (target_image != (Image *) NULL) \
3897 target_image=DestroyImage(target_image); \
3898 if (threshold_image != (Image *) NULL) \
3899 threshold_image=DestroyImage(threshold_image); \
3900 if (trx_image != (Image *) NULL) \
3901 trx_image=DestroyImage(trx_image); \
3902 if (try_image != (Image *) NULL) \
3903 try_image=DestroyImage(try_image); \
3904 if (tx_image != (Image *) NULL) \
3905 tx_image=DestroyImage(tx_image); \
3906 if (ty_image != (Image *) NULL) \
3907 ty_image=DestroyImage(ty_image); \
3908 return((Image *) NULL); \
3915 standard_deviation = 0.0;
3918 *dot_product_image = (Image *) NULL,
3919 *magnitude_image = (Image *) NULL,
3920 *reconstruct_image = (Image *) NULL,
3921 *rx_image = (Image *) NULL,
3922 *ry_image = (Image *) NULL,
3923 *trx_image = (Image *) NULL,
3924 *target_image = (Image *) NULL,
3925 *threshold_image = (Image *) NULL,
3926 *try_image = (Image *) NULL,
3927 *tx_image = (Image *) NULL,
3928 *ty_image = (Image *) NULL;
3931 status = MagickTrue;
3939 target_image=CloneImage(image,0,0,MagickTrue,exception);
3940 if (target_image == (Image *) NULL)
3941 return((Image *) NULL);
3945 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3946 if (reconstruct_image == (Image *) NULL)
3947 ThrowDPCSimilarityException();
3951 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3953 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3954 if (rx_image == (Image *) NULL)
3955 ThrowDPCSimilarityException();
3956 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3957 reconstruct_image=DestroyImage(reconstruct_image);
3958 if (ry_image == (Image *) NULL)
3959 ThrowDPCSimilarityException();
3963 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3964 if (magnitude_image == (Image *) NULL)
3965 ThrowDPCSimilarityException();
3969 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3970 if (threshold_image == (Image *) NULL)
3971 ThrowDPCSimilarityException();
3972 status=BilevelImage(threshold_image,0.0,exception);
3973 if (status == MagickFalse)
3974 ThrowDPCSimilarityException();
3975 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3976 threshold_image=DestroyImage(threshold_image);
3977 if (status == MagickFalse)
3978 ThrowDPCSimilarityException();
3979 edge_factor=MagickSafeReciprocal(QuantumScale*mean*(
double)
3980 reconstruct->columns*(
double) reconstruct->rows)+QuantumScale;
3984 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3985 rx_image=DestroyImage(rx_image);
3986 if (trx_image == (Image *) NULL)
3987 ThrowDPCSimilarityException();
3989 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3990 magnitude_image=DestroyImage(magnitude_image);
3991 ry_image=DestroyImage(ry_image);
3992 if (try_image == (Image *) NULL)
3993 ThrowDPCSimilarityException();
3998 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
4000 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
4001 if (tx_image == (Image *) NULL)
4002 ThrowDPCSimilarityException();
4003 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
4004 target_image=DestroyImage(target_image);
4005 if (ty_image == (Image *) NULL)
4006 ThrowDPCSimilarityException();
4010 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
4011 if (magnitude_image == (Image *) NULL)
4012 ThrowDPCSimilarityException();
4016 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
4017 tx_image=DestroyImage(tx_image);
4018 if (trx_image == (Image *) NULL)
4019 ThrowDPCSimilarityException();
4021 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
4022 ty_image=DestroyImage(ty_image);
4023 magnitude_image=DestroyImage(magnitude_image);
4024 if (try_image == (Image *) NULL)
4025 ThrowDPCSimilarityException();
4030 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
4031 rx_image=DestroyImage(rx_image);
4032 tx_image=DestroyImage(tx_image);
4033 if (trx_image == (Image *) NULL)
4034 ThrowDPCSimilarityException();
4035 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
4036 ry_image=DestroyImage(ry_image);
4037 ty_image=DestroyImage(ty_image);
4038 if (try_image == (Image *) NULL)
4039 ThrowDPCSimilarityException();
4043 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
4044 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
4046 try_image=DestroyImage(try_image);
4047 if (status == MagickFalse)
4048 ThrowDPCSimilarityException();
4049 status=SIMMultiplyImage(trx_image,edge_factor,
4050 (
const ChannelStatistics *) NULL,exception);
4051 if (status == MagickFalse)
4052 ThrowDPCSimilarityException();
4056 SetGeometry(image,&geometry);
4057 geometry.width=image->columns;
4058 geometry.height=image->rows;
4059 (void) ResetImagePage(trx_image,
"0x0+0+0");
4060 dot_product_image=CropImage(trx_image,&geometry,exception);
4061 trx_image=DestroyImage(trx_image);
4062 if (dot_product_image == (Image *) NULL)
4063 ThrowDPCSimilarityException();
4064 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4068 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4070 if (status == MagickFalse)
4071 ThrowDPCSimilarityException();
4072 dot_product_image->depth=32;
4073 dot_product_image->colorspace=GRAYColorspace;
4074 dot_product_image->alpha_trait=UndefinedPixelTrait;
4075 status=SIMFilterImageNaNs(dot_product_image,exception);
4076 if (status == MagickFalse)
4077 ThrowDPCSimilarityException();
4078 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4079 if (status == MagickFalse)
4080 ThrowDPCSimilarityException();
4081 if ((QuantumScale*maxima) > 1.0)
4083 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4084 (
const ChannelStatistics *) NULL,exception);
4085 maxima=(double) QuantumRange;
4087 *similarity_metric=QuantumScale*maxima;
4088 return(dot_product_image);
4091static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4092 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4094#define ThrowMSESimilarityException() \
4096 if (alpha_image != (Image *) NULL) \
4097 alpha_image=DestroyImage(alpha_image); \
4098 if (beta_image != (Image *) NULL) \
4099 beta_image=DestroyImage(beta_image); \
4100 if (channel_statistics != (ChannelStatistics *) NULL) \
4101 channel_statistics=(ChannelStatistics *) \
4102 RelinquishMagickMemory(channel_statistics); \
4103 if (mean_image != (Image *) NULL) \
4104 mean_image=DestroyImage(mean_image); \
4105 if (mse_image != (Image *) NULL) \
4106 mse_image=DestroyImage(mse_image); \
4107 if (reconstruct_image != (Image *) NULL) \
4108 reconstruct_image=DestroyImage(reconstruct_image); \
4109 if (sum_image != (Image *) NULL) \
4110 sum_image=DestroyImage(sum_image); \
4111 if (alpha_image != (Image *) NULL) \
4112 alpha_image=DestroyImage(alpha_image); \
4113 return((Image *) NULL); \
4117 *channel_statistics = (ChannelStatistics *) NULL;
4123 *alpha_image = (Image *) NULL,
4124 *beta_image = (Image *) NULL,
4125 *mean_image = (Image *) NULL,
4126 *mse_image = (Image *) NULL,
4127 *reconstruct_image = (Image *) NULL,
4128 *sum_image = (Image *) NULL,
4129 *target_image = (Image *) NULL;
4132 status = MagickTrue;
4140 target_image=SIMSquareImage(image,exception);
4141 if (target_image == (Image *) NULL)
4142 ThrowMSESimilarityException();
4143 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4144 if (reconstruct_image == (Image *) NULL)
4145 ThrowMSESimilarityException();
4149 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4151 target_image=DestroyImage(target_image);
4152 if (alpha_image == (Image *) NULL)
4153 ThrowMSESimilarityException();
4154 status=SIMMultiplyImage(alpha_image,1.0/(
double) reconstruct->columns/(
double)
4155 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4156 if (status == MagickFalse)
4157 ThrowMSESimilarityException();
4161 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4162 MagickTrue,0,0,exception);
4163 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4164 if (beta_image == (Image *) NULL)
4166 reconstruct_image=DestroyImage(reconstruct_image);
4167 ThrowMSESimilarityException();
4169 status=SIMMultiplyImage(beta_image,-2.0/(
double) reconstruct->columns/(
double)
4170 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4171 reconstruct_image=DestroyImage(reconstruct_image);
4172 if (status == MagickFalse)
4173 ThrowMSESimilarityException();
4177 sum_image=SIMSquareImage(reconstruct,exception);
4178 if (sum_image == (Image *) NULL)
4179 ThrowMSESimilarityException();
4180 channel_statistics=GetImageStatistics(sum_image,exception);
4181 if (channel_statistics == (ChannelStatistics *) NULL)
4182 ThrowMSESimilarityException();
4183 status=SetImageStorageClass(sum_image,DirectClass,exception);
4184 if (status == MagickFalse)
4185 ThrowMSESimilarityException();
4186 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4187 channel_statistics=(ChannelStatistics *)
4188 RelinquishMagickMemory(channel_statistics);
4189 if (status == MagickFalse)
4190 ThrowMSESimilarityException();
4194 AppendImageToList(&sum_image,alpha_image);
4195 AppendImageToList(&sum_image,beta_image);
4196 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4197 if (mean_image == (Image *) NULL)
4198 ThrowMSESimilarityException();
4199 sum_image=DestroyImage(sum_image);
4200 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4201 if (status == MagickFalse)
4202 ThrowMSESimilarityException();
4206 SetGeometry(image,&geometry);
4207 geometry.width=image->columns;
4208 geometry.height=image->rows;
4209 (void) ResetImagePage(mean_image,
"0x0+0+0");
4210 mse_image=CropImage(mean_image,&geometry,exception);
4211 mean_image=DestroyImage(mean_image);
4212 if (mse_image == (Image *) NULL)
4213 ThrowMSESimilarityException();
4217 (void) ResetImagePage(mse_image,
"0x0+0+0");
4218 (void) ClampImage(mse_image,exception);
4219 mse_image->depth=32;
4220 mse_image->colorspace=GRAYColorspace;
4221 mse_image->alpha_trait=UndefinedPixelTrait;
4222 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4223 if (status == MagickFalse)
4224 ThrowMSESimilarityException();
4225 status=NegateImage(mse_image,MagickFalse,exception);
4226 if (status == MagickFalse)
4227 ThrowMSESimilarityException();
4228 alpha_image=DestroyImage(alpha_image);
4229 beta_image=DestroyImage(beta_image);
4230 if ((QuantumScale*minima) < (
double) FLT_EPSILON)
4232 *similarity_metric=QuantumScale*minima;
4236static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4237 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4239#define ThrowNCCSimilarityException() \
4241 if (alpha_image != (Image *) NULL) \
4242 alpha_image=DestroyImage(alpha_image); \
4243 if (beta_image != (Image *) NULL) \
4244 beta_image=DestroyImage(beta_image); \
4245 if (channel_statistics != (ChannelStatistics *) NULL) \
4246 channel_statistics=(ChannelStatistics *) \
4247 RelinquishMagickMemory(channel_statistics); \
4248 if (correlation_image != (Image *) NULL) \
4249 correlation_image=DestroyImage(correlation_image); \
4250 if (divide_image != (Image *) NULL) \
4251 divide_image=DestroyImage(divide_image); \
4252 if (ncc_image != (Image *) NULL) \
4253 ncc_image=DestroyImage(ncc_image); \
4254 if (normalize_image != (Image *) NULL) \
4255 normalize_image=DestroyImage(normalize_image); \
4256 if (reconstruct_image != (Image *) NULL) \
4257 reconstruct_image=DestroyImage(reconstruct_image); \
4258 if (target_image != (Image *) NULL) \
4259 target_image=DestroyImage(target_image); \
4260 if (variance_image != (Image *) NULL) \
4261 variance_image=DestroyImage(variance_image); \
4262 return((Image *) NULL); \
4266 *channel_statistics = (ChannelStatistics *) NULL;
4272 *alpha_image = (Image *) NULL,
4273 *beta_image = (Image *) NULL,
4274 *correlation_image = (Image *) NULL,
4275 *divide_image = (Image *) NULL,
4276 *ncc_image = (Image *) NULL,
4277 *normalize_image = (Image *) NULL,
4278 *reconstruct_image = (Image *) NULL,
4279 *target_image = (Image *) NULL,
4280 *variance_image = (Image *) NULL;
4283 status = MagickTrue;
4291 target_image=SIMSquareImage(image,exception);
4292 if (target_image == (Image *) NULL)
4293 ThrowNCCSimilarityException();
4294 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4295 if (reconstruct_image == (Image *) NULL)
4296 ThrowNCCSimilarityException();
4300 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4302 target_image=DestroyImage(target_image);
4303 if (alpha_image == (Image *) NULL)
4304 ThrowNCCSimilarityException();
4305 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4306 (
double) reconstruct->columns*(
double) reconstruct->rows,
4307 (
const ChannelStatistics *) NULL,exception);
4308 if (status == MagickFalse)
4309 ThrowNCCSimilarityException();
4313 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4314 reconstruct_image=DestroyImage(reconstruct_image);
4315 if (beta_image == (Image *) NULL)
4316 ThrowNCCSimilarityException();
4317 target_image=SIMSquareImage(beta_image,exception);
4318 beta_image=DestroyImage(beta_image);
4319 if (target_image == (Image *) NULL)
4320 ThrowNCCSimilarityException();
4321 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4322 (
const ChannelStatistics *) NULL,exception);
4323 if (status == MagickFalse)
4324 ThrowNCCSimilarityException();
4328 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4329 target_image=DestroyImage(target_image);
4330 alpha_image=DestroyImage(alpha_image);
4331 if (variance_image == (Image *) NULL)
4332 ThrowNCCSimilarityException();
4336 channel_statistics=GetImageStatistics(reconstruct,exception);
4337 if (channel_statistics == (ChannelStatistics *) NULL)
4338 ThrowNCCSimilarityException();
4339 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4340 if (status == MagickFalse)
4341 ThrowNCCSimilarityException();
4342 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4344 channel_statistics=(ChannelStatistics *)
4345 RelinquishMagickMemory(channel_statistics);
4346 if (normalize_image == (Image *) NULL)
4347 ThrowNCCSimilarityException();
4348 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4349 normalize_image=DestroyImage(normalize_image);
4350 if (correlation_image == (Image *) NULL)
4351 ThrowNCCSimilarityException();
4355 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4356 correlation_image=DestroyImage(correlation_image);
4357 variance_image=DestroyImage(variance_image);
4358 if (divide_image == (Image *) NULL)
4359 ThrowNCCSimilarityException();
4363 SetGeometry(image,&geometry);
4364 geometry.width=image->columns;
4365 geometry.height=image->rows;
4366 (void) ResetImagePage(divide_image,
"0x0+0+0");
4367 ncc_image=CropImage(divide_image,&geometry,exception);
4368 divide_image=DestroyImage(divide_image);
4369 if (ncc_image == (Image *) NULL)
4370 ThrowNCCSimilarityException();
4374 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4375 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4376 if (status == MagickFalse)
4377 ThrowNCCSimilarityException();
4378 ncc_image->depth=32;
4379 ncc_image->colorspace=GRAYColorspace;
4380 ncc_image->alpha_trait=UndefinedPixelTrait;
4381 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4382 if (status == MagickFalse)
4383 ThrowNCCSimilarityException();
4384 if ((QuantumScale*maxima) > 1.0)
4386 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4387 (
const ChannelStatistics *) NULL,exception);
4388 maxima=(double) QuantumRange;
4390 *similarity_metric=QuantumScale*maxima;
4394static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4395 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4397#define ThrowPhaseSimilarityException() \
4399 if (phase_image != (Image *) NULL) \
4400 phase_image=DestroyImage(phase_image); \
4401 if (gamma_image != (Image *) NULL) \
4402 gamma_image=DestroyImage(gamma_image); \
4403 if (test_magnitude != (Image *) NULL) \
4404 test_magnitude=DestroyImage(test_magnitude); \
4405 if (magnitude_image != (Image *) NULL) \
4406 magnitude_image=DestroyImage(magnitude_image); \
4407 if (reconstruct_magnitude != (Image *) NULL) \
4408 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4409 if (correlation_image != (Image *) NULL) \
4410 correlation_image=DestroyImage(correlation_image); \
4411 if (fft_images != (Image *) NULL) \
4412 fft_images=DestroyImageList(fft_images); \
4413 if (reconstruct_image != (Image *) NULL) \
4414 reconstruct_image=DestroyImage(reconstruct_image); \
4415 if (target_image != (Image *) NULL) \
4416 target_image=DestroyImage(target_image); \
4417 return((Image *) NULL); \
4424 *correlation_image = (Image *) NULL,
4425 *fft_images = (Image *) NULL,
4426 *gamma_image = (Image *) NULL,
4427 *magnitude_image = (Image *) NULL,
4428 *phase_image = (Image *) NULL,
4429 *reconstruct_image = (Image *) NULL,
4430 *reconstruct_magnitude = (Image *) NULL,
4431 *target_image = (Image *) NULL,
4432 *test_magnitude = (Image *) NULL;
4435 status = MagickTrue;
4443 target_image=CloneImage(image,0,0,MagickTrue,exception);
4444 if (target_image == (Image *) NULL)
4445 ThrowPhaseSimilarityException();
4446 (void) ResetImagePage(target_image,
"0x0+0+0");
4447 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4448 &target_image->background_color);
4449 status=SetImageExtent(target_image,2*CastDoubleToSizeT(ceil((
double)
4450 image->columns/2.0)),2*CastDoubleToSizeT(ceil((
double) image->rows/2.0)),
4452 if (status == MagickFalse)
4453 ThrowPhaseSimilarityException();
4457 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4458 if (reconstruct_image == (Image *) NULL)
4459 ThrowPhaseSimilarityException();
4460 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4461 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4462 &reconstruct_image->background_color);
4463 status=SetImageExtent(reconstruct_image,2*CastDoubleToSizeT(ceil((
double)
4464 image->columns/2.0)),2*CastDoubleToSizeT(ceil((
double) image->rows/2.0)),
4466 if (status == MagickFalse)
4467 ThrowPhaseSimilarityException();
4471 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4472 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4473 if (fft_images == (Image *) NULL)
4474 ThrowPhaseSimilarityException();
4475 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4476 fft_images=DestroyImageList(fft_images);
4477 if (test_magnitude == (Image *) NULL)
4478 ThrowPhaseSimilarityException();
4479 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4480 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4482 if (fft_images == (Image *) NULL)
4483 ThrowPhaseSimilarityException();
4484 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4485 fft_images=DestroyImageList(fft_images);
4486 if (reconstruct_magnitude == (Image *) NULL)
4487 ThrowPhaseSimilarityException();
4488 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4489 if (magnitude_image == (Image *) NULL)
4490 ThrowPhaseSimilarityException();
4491 DisableCompositeClampUnlessSpecified(magnitude_image);
4492 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4493 MagickTrue,0,0,exception);
4497 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4498 magnitude_image,exception);
4499 target_image=DestroyImage(target_image);
4500 reconstruct_image=DestroyImage(reconstruct_image);
4501 test_magnitude=DestroyImage(test_magnitude);
4502 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4503 if (correlation_image == (Image *) NULL)
4504 ThrowPhaseSimilarityException();
4505 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4506 correlation_image=DestroyImage(correlation_image);
4507 if (gamma_image == (Image *) NULL)
4508 ThrowPhaseSimilarityException();
4512 SetGeometry(image,&geometry);
4513 geometry.width=image->columns;
4514 geometry.height=image->rows;
4515 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4516 phase_image=CropImage(gamma_image,&geometry,exception);
4517 gamma_image=DestroyImage(gamma_image);
4518 if (phase_image == (Image *) NULL)
4519 ThrowPhaseSimilarityException();
4520 (void) ResetImagePage(phase_image,
"0x0+0+0");
4524 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4525 if (status == MagickFalse)
4526 ThrowPhaseSimilarityException();
4527 phase_image->depth=32;
4528 phase_image->colorspace=GRAYColorspace;
4529 phase_image->alpha_trait=UndefinedPixelTrait;
4530 status=SIMFilterImageNaNs(phase_image,exception);
4531 if (status == MagickFalse)
4532 ThrowPhaseSimilarityException();
4533 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4534 if (status == MagickFalse)
4535 ThrowPhaseSimilarityException();
4536 magnitude_image=DestroyImage(magnitude_image);
4537 *similarity_metric=QuantumScale*maxima;
4538 return(phase_image);
4541static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4542 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4545 *psnr_image = (Image *) NULL;
4547 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4549 if (psnr_image == (Image *) NULL)
4551 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4552 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4556static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4557 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4560 *rmse_image = (Image *) NULL;
4562 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4564 if (rmse_image == (Image *) NULL)
4566 *similarity_metric=sqrt(*similarity_metric);
4571static double GetSimilarityMetric(
const Image *image,
4572 const Image *reconstruct_image,
const MetricType metric,
4573 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4576 *channel_similarity,
4580 *sans_exception = AcquireExceptionInfo();
4586 status = MagickTrue;
4592 length = MaxPixelChannels+1UL;
4594 SetGeometry(reconstruct_image,&geometry);
4595 geometry.x=x_offset;
4596 geometry.y=y_offset;
4597 similarity_image=CropImage(image,&geometry,sans_exception);
4598 sans_exception=DestroyExceptionInfo(sans_exception);
4599 if (similarity_image == (Image *) NULL)
4604 channel_similarity=(
double *) AcquireQuantumMemory(length,
4605 sizeof(*channel_similarity));
4606 if (channel_similarity == (
double *) NULL)
4608 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4611 case AbsoluteErrorMetric:
4613 status=GetAESimilarity(similarity_image,reconstruct_image,
4614 channel_similarity,exception);
4617 case DotProductCorrelationErrorMetric:
4619 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4620 channel_similarity,exception);
4623 case FuzzErrorMetric:
4625 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4626 channel_similarity,exception);
4629 case MeanAbsoluteErrorMetric:
4631 status=GetMAESimilarity(similarity_image,reconstruct_image,
4632 channel_similarity,exception);
4635 case MeanErrorPerPixelErrorMetric:
4637 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4638 channel_similarity,exception);
4641 case MeanSquaredErrorMetric:
4643 status=GetMSESimilarity(similarity_image,reconstruct_image,
4644 channel_similarity,exception);
4647 case NormalizedCrossCorrelationErrorMetric:
4649 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4650 channel_similarity,exception);
4653 case PeakAbsoluteErrorMetric:
4655 status=GetPASimilarity(similarity_image,reconstruct_image,
4656 channel_similarity,exception);
4659 case PeakSignalToNoiseRatioErrorMetric:
4661 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4662 channel_similarity,exception);
4665 case PerceptualHashErrorMetric:
4667 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4668 channel_similarity,exception);
4671 case PhaseCorrelationErrorMetric:
4673 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4674 channel_similarity,exception);
4677 case PixelDifferenceCountErrorMetric:
4679 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4680 channel_similarity,exception);
4683 case RootMeanSquaredErrorMetric:
4684 case UndefinedErrorMetric:
4687 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4688 channel_similarity,exception);
4691 case StructuralDissimilarityErrorMetric:
4693 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4694 channel_similarity,exception);
4697 case StructuralSimilarityErrorMetric:
4699 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4700 channel_similarity,exception);
4704 similarity_image=DestroyImage(similarity_image);
4705 similarity=channel_similarity[CompositePixelChannel];
4706 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4707 if (status == MagickFalse)
4712MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4713 const MetricType metric,
const double similarity_threshold,
4714 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4716#define SimilarityImageTag "Similarity/Image"
4732 *similarity_image = (Image *) NULL;
4735 status = MagickTrue;
4741 similarity_info = { 0.0, 0, 0 };
4750 assert(image != (
const Image *) NULL);
4751 assert(image->signature == MagickCoreSignature);
4752 assert(exception != (ExceptionInfo *) NULL);
4753 assert(exception->signature == MagickCoreSignature);
4754 assert(offset != (RectangleInfo *) NULL);
4755 if (IsEventLogging() != MagickFalse)
4756 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4757 SetGeometry(reconstruct,offset);
4758 *similarity_metric=0.0;
4761#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4763 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4764 if (artifact == (
const char *) NULL)
4765 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4766 if (((artifact == (
const char *) NULL) ||
4767 (IsStringTrue(artifact) != MagickFalse)) &&
4768 ((image->channels & ReadMaskChannel) == 0))
4771 case DotProductCorrelationErrorMetric:
4773 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4774 similarity_metric,exception);
4775 return(similarity_image);
4777 case MeanSquaredErrorMetric:
4779 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4780 similarity_metric,exception);
4781 return(similarity_image);
4783 case NormalizedCrossCorrelationErrorMetric:
4785 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4786 similarity_metric,exception);
4787 return(similarity_image);
4789 case PeakSignalToNoiseRatioErrorMetric:
4791 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4792 similarity_metric,exception);
4793 return(similarity_image);
4795 case PhaseCorrelationErrorMetric:
4797 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4798 similarity_metric,exception);
4799 return(similarity_image);
4801 case RootMeanSquaredErrorMetric:
4802 case UndefinedErrorMetric:
4804 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4805 similarity_metric,exception);
4806 return(similarity_image);
4813 if ((image->columns < reconstruct->columns) ||
4814 (image->rows < reconstruct->rows))
4816 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4817 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4818 return((Image *) NULL);
4820 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4821 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4822 if (similarity_image == (Image *) NULL)
4823 return((Image *) NULL);
4824 similarity_image->depth=32;
4825 similarity_image->colorspace=GRAYColorspace;
4826 similarity_image->alpha_trait=UndefinedPixelTrait;
4827 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4828 if (status == MagickFalse)
4829 return(DestroyImage(similarity_image));
4833 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4834 similarity_info.x,similarity_info.y,exception);
4835 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4836#if defined(MAGICKCORE_OPENMP_SUPPORT)
4837 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4838 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4840 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4846 threshold_trigger = MagickFalse;
4852 channel_info = similarity_info;
4857 if (status == MagickFalse)
4859 if (threshold_trigger != MagickFalse)
4861 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4862 similarity_image->columns,1,exception);
4863 if (q == (Quantum *) NULL)
4868 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4873 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4877 case DotProductCorrelationErrorMetric:
4878 case NormalizedCrossCorrelationErrorMetric:
4879 case PeakSignalToNoiseRatioErrorMetric:
4880 case PhaseCorrelationErrorMetric:
4881 case StructuralSimilarityErrorMetric:
4883 if (similarity <= channel_info.similarity)
4885 channel_info.similarity=similarity;
4892 if (similarity >= channel_info.similarity)
4894 channel_info.similarity=similarity;
4900 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4902 PixelChannel channel = GetPixelChannelChannel(image,i);
4903 PixelTrait traits = GetPixelChannelTraits(image,channel);
4904 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4906 if (((traits & UpdatePixelTrait) == 0) ||
4907 ((similarity_traits & UpdatePixelTrait) == 0))
4911 case DotProductCorrelationErrorMetric:
4912 case NormalizedCrossCorrelationErrorMetric:
4913 case PeakSignalToNoiseRatioErrorMetric:
4914 case PhaseCorrelationErrorMetric:
4915 case StructuralSimilarityErrorMetric:
4917 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4918 QuantumRange*similarity),q);
4923 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4924 QuantumRange*(1.0-similarity)),q);
4929 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4931#if defined(MAGICKCORE_OPENMP_SUPPORT)
4932 #pragma omp critical (MagickCore_GetSimilarityMetric)
4936 case DotProductCorrelationErrorMetric:
4937 case NormalizedCrossCorrelationErrorMetric:
4938 case PeakSignalToNoiseRatioErrorMetric:
4939 case PhaseCorrelationErrorMetric:
4940 case StructuralSimilarityErrorMetric:
4942 if (similarity_threshold != DefaultSimilarityThreshold)
4943 if (channel_info.similarity >= similarity_threshold)
4944 threshold_trigger=MagickTrue;
4945 if (channel_info.similarity >= similarity_info.similarity)
4946 similarity_info=channel_info;
4951 if (similarity_threshold != DefaultSimilarityThreshold)
4952 if (channel_info.similarity < similarity_threshold)
4953 threshold_trigger=MagickTrue;
4954 if (channel_info.similarity < similarity_info.similarity)
4955 similarity_info=channel_info;
4959 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4961 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4967 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4968 if (proceed == MagickFalse)
4972 similarity_view=DestroyCacheView(similarity_view);
4973 if (status == MagickFalse)
4974 similarity_image=DestroyImage(similarity_image);
4975 *similarity_metric=similarity_info.similarity;
4976 if (fabs(*similarity_metric) < MagickEpsilon)
4977 *similarity_metric=0.0;
4978 offset->x=similarity_info.x;
4979 offset->y=similarity_info.y;
4980 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4981 GetMagickPrecision(),*similarity_metric);
4982 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4983 GetMagickPrecision(),(
double) offset->x);
4984 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4985 GetMagickPrecision(),(
double) offset->y);
4986 return(similarity_image);