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)
330 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
331 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
332 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
333 image_view=AcquireVirtualCacheView(image,exception);
334 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
335#if defined(MMAGICKCORE_OPENMP_SUPPORT)
336 #pragma omp parallel for schedule(static) shared(similarity,status) \
337 magick_number_threads(image,image,rows,1)
339 for (y=0; y < (ssize_t) rows; y++)
346 channel_similarity[MaxPixelChannels+1] = { 0.0 };
351 if (status == MagickFalse)
353 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
354 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
355 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
360 for (x=0; x < (ssize_t) columns; x++)
369 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
370 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
372 p+=(ptrdiff_t) GetPixelChannels(image);
373 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
376 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
377 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
378 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
383 PixelChannel channel = GetPixelChannelChannel(image,i);
384 PixelTrait traits = GetPixelChannelTraits(image,channel);
385 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
387 if (((traits & UpdatePixelTrait) == 0) ||
388 ((reconstruct_traits & UpdatePixelTrait) == 0))
390 if (channel == AlphaPixelChannel)
391 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
394 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
395 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
397 double ae = fabs(error);
398 channel_similarity[i]+=ae;
399 channel_similarity[CompositePixelChannel]+=ae;
402 p+=(ptrdiff_t) GetPixelChannels(image);
403 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
405#if defined(MAGICKCORE_OPENMP_SUPPORT)
406 #pragma omp critical (MagickCore_GetAESimilarity)
412 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
414 PixelChannel channel = GetPixelChannelChannel(image,j);
415 PixelTrait traits = GetPixelChannelTraits(image,channel);
416 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
418 if (((traits & UpdatePixelTrait) == 0) ||
419 ((reconstruct_traits & UpdatePixelTrait) == 0))
421 similarity[j]+=channel_similarity[j];
423 similarity[CompositePixelChannel]+=
424 channel_similarity[CompositePixelChannel];
427 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
428 reconstruct_view=DestroyCacheView(reconstruct_view);
429 image_view=DestroyCacheView(image_view);
433static MagickBooleanType GetDPCSimilarity(
const Image *image,
434 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
436#define SimilarityImageTag "Similarity/Image"
444 *reconstruct_statistics;
447 norm[MaxPixelChannels+1] = { 0.0 },
448 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
467 image_statistics=GetImageStatistics(image,exception);
468 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
469 if ((image_statistics == (ChannelStatistics *) NULL) ||
470 (reconstruct_statistics == (ChannelStatistics *) NULL))
472 if (image_statistics != (ChannelStatistics *) NULL)
473 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
475 if (reconstruct_statistics != (ChannelStatistics *) NULL)
476 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
477 reconstruct_statistics);
480 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
481 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
482 image_view=AcquireVirtualCacheView(image,exception);
483 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
484#if defined(MAGICKCORE_OPENMP_SUPPORT)
485 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
486 magick_number_threads(image,image,rows,1)
488 for (y=0; y < (ssize_t) rows; y++)
495 channel_norm[MaxPixelChannels+1] = { 0.0 },
496 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
497 channel_similarity[MaxPixelChannels+1] = { 0.0 };
502 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
503 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
504 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
509 for (x=0; x < (ssize_t) columns; x++)
518 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
519 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
521 p+=(ptrdiff_t) GetPixelChannels(image);
522 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
525 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
526 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
527 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
533 PixelChannel channel = GetPixelChannelChannel(image,i);
534 PixelTrait traits = GetPixelChannelTraits(image,channel);
535 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
537 if (((traits & UpdatePixelTrait) == 0) ||
538 ((reconstruct_traits & UpdatePixelTrait) == 0))
540 if (channel == AlphaPixelChannel)
542 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
543 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
544 channel,q)-reconstruct_statistics[channel].mean);
548 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
549 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
550 q)-reconstruct_statistics[channel].mean);
552 channel_similarity[i]+=alpha*beta;
553 channel_norm[i]+=alpha*alpha;
554 channel_reconstruct_norm[i]+=beta*beta;
556 p+=(ptrdiff_t) GetPixelChannels(image);
557 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
559#if defined(MAGICKCORE_OPENMP_SUPPORT)
560 #pragma omp critical (MagickCore_GetDPCSimilarity)
566 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
568 PixelChannel channel = GetPixelChannelChannel(image,j);
569 PixelTrait traits = GetPixelChannelTraits(image,channel);
570 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
572 if (((traits & UpdatePixelTrait) == 0) ||
573 ((reconstruct_traits & UpdatePixelTrait) == 0))
575 similarity[j]+=channel_similarity[j];
576 similarity[CompositePixelChannel]+=channel_similarity[j];
577 norm[j]+=channel_norm[j];
578 norm[CompositePixelChannel]+=channel_norm[j];
579 reconstruct_norm[j]+=channel_reconstruct_norm[j];
580 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
583 if (image->progress_monitor != (MagickProgressMonitor) NULL)
588#if defined(MAGICKCORE_OPENMP_SUPPORT)
592 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
593 if (proceed == MagickFalse)
600 reconstruct_view=DestroyCacheView(reconstruct_view);
601 image_view=DestroyCacheView(image_view);
605 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
607 PixelChannel channel = GetPixelChannelChannel(image,k);
608 PixelTrait traits = GetPixelChannelTraits(image,channel);
609 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
611 if (((traits & UpdatePixelTrait) == 0) ||
612 ((reconstruct_traits & UpdatePixelTrait) == 0))
614 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
616 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
617 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
621 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
622 reconstruct_statistics);
623 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
628static MagickBooleanType GetFUZZSimilarity(
const Image *image,
629 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
653 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
654 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
655 image_view=AcquireVirtualCacheView(image,exception);
656 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
657#if defined(MAGICKCORE_OPENMP_SUPPORT)
658 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
659 magick_number_threads(image,image,rows,1)
661 for (y=0; y < (ssize_t) rows; y++)
669 channel_similarity[MaxPixelChannels+1] = { 0.0 };
674 if (status == MagickFalse)
676 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
677 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
678 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
683 for (x=0; x < (ssize_t) columns; x++)
692 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
693 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
695 p+=(ptrdiff_t) GetPixelChannels(image);
696 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
699 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
700 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
701 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
706 PixelChannel channel = GetPixelChannelChannel(image,i);
707 PixelTrait traits = GetPixelChannelTraits(image,channel);
708 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
710 if (((traits & UpdatePixelTrait) == 0) ||
711 ((reconstruct_traits & UpdatePixelTrait) == 0))
713 if (channel == AlphaPixelChannel)
714 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
717 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
718 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
720 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
721 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
726 p+=(ptrdiff_t) GetPixelChannels(image);
727 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
729#if defined(MAGICKCORE_OPENMP_SUPPORT)
730 #pragma omp critical (MagickCore_GetFUZZSimilarity)
737 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
739 PixelChannel channel = GetPixelChannelChannel(image,j);
740 PixelTrait traits = GetPixelChannelTraits(image,channel);
741 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
743 if (((traits & UpdatePixelTrait) == 0) ||
744 ((reconstruct_traits & UpdatePixelTrait) == 0))
746 similarity[j]+=channel_similarity[j];
748 similarity[CompositePixelChannel]+=
749 channel_similarity[CompositePixelChannel];
752 reconstruct_view=DestroyCacheView(reconstruct_view);
753 image_view=DestroyCacheView(image_view);
754 area=MagickSafeReciprocal(area);
755 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
757 PixelChannel channel = GetPixelChannelChannel(image,k);
758 PixelTrait traits = GetPixelChannelTraits(image,channel);
759 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
761 if (((traits & UpdatePixelTrait) == 0) ||
762 ((reconstruct_traits & UpdatePixelTrait) == 0))
766 similarity[CompositePixelChannel]*=area;
770static MagickBooleanType GetMAESimilarity(
const Image *image,
771 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
794 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
795 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
796 image_view=AcquireVirtualCacheView(image,exception);
797 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
798#if defined(MAGICKCORE_OPENMP_SUPPORT)
799 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
800 magick_number_threads(image,image,rows,1)
802 for (y=0; y < (ssize_t) rows; y++)
810 channel_similarity[MaxPixelChannels+1] = { 0.0 };
815 if (status == MagickFalse)
817 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
818 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
819 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
824 for (x=0; x < (ssize_t) columns; x++)
833 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
834 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
836 p+=(ptrdiff_t) GetPixelChannels(image);
837 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
840 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
841 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
842 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
847 PixelChannel channel = GetPixelChannelChannel(image,i);
848 PixelTrait traits = GetPixelChannelTraits(image,channel);
849 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
851 if (((traits & UpdatePixelTrait) == 0) ||
852 ((reconstruct_traits & UpdatePixelTrait) == 0))
854 if (channel == AlphaPixelChannel)
855 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
856 reconstruct_image,channel,q));
858 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
860 channel_similarity[i]+=error;
861 channel_similarity[CompositePixelChannel]+=error;
864 p+=(ptrdiff_t) GetPixelChannels(image);
865 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
867#if defined(MAGICKCORE_OPENMP_SUPPORT)
868 #pragma omp critical (MagickCore_GetMAESimilarity)
875 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
877 PixelChannel channel = GetPixelChannelChannel(image,j);
878 PixelTrait traits = GetPixelChannelTraits(image,channel);
879 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
881 if (((traits & UpdatePixelTrait) == 0) ||
882 ((reconstruct_traits & UpdatePixelTrait) == 0))
884 similarity[j]+=channel_similarity[j];
886 similarity[CompositePixelChannel]+=
887 channel_similarity[CompositePixelChannel];
890 reconstruct_view=DestroyCacheView(reconstruct_view);
891 image_view=DestroyCacheView(image_view);
892 area=MagickSafeReciprocal(area);
893 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
895 PixelChannel channel = GetPixelChannelChannel(image,k);
896 PixelTrait traits = GetPixelChannelTraits(image,channel);
897 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
899 if (((traits & UpdatePixelTrait) == 0) ||
900 ((reconstruct_traits & UpdatePixelTrait) == 0))
904 similarity[CompositePixelChannel]*=area;
905 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
909static MagickBooleanType GetMEPPSimilarity(Image *image,
910 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
918 maximum_error = -MagickMaximumValue,
935 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
936 image_view=AcquireVirtualCacheView(image,exception);
937 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
938#if defined(MAGICKCORE_OPENMP_SUPPORT)
939 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
940 magick_number_threads(image,image,rows,1)
942 for (y=0; y < (ssize_t) rows; y++)
950 channel_similarity[MaxPixelChannels+1] = { 0.0 },
951 channel_maximum_error = maximum_error,
952 channel_mean_error = 0.0;
957 if (status == MagickFalse)
959 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
960 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
961 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
966 for (x=0; x < (ssize_t) columns; x++)
975 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
976 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
978 p+=(ptrdiff_t) GetPixelChannels(image);
979 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
982 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
983 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
984 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
989 PixelChannel channel = GetPixelChannelChannel(image,i);
990 PixelTrait traits = GetPixelChannelTraits(image,channel);
991 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
993 if (((traits & UpdatePixelTrait) == 0) ||
994 ((reconstruct_traits & UpdatePixelTrait) == 0))
996 if (channel == AlphaPixelChannel)
997 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
998 reconstruct_image,channel,q));
1000 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1002 channel_similarity[i]+=error;
1003 channel_similarity[CompositePixelChannel]+=error;
1004 channel_mean_error+=error*error;
1005 if (error > channel_maximum_error)
1006 channel_maximum_error=error;
1009 p+=(ptrdiff_t) GetPixelChannels(image);
1010 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1012#if defined(MAGICKCORE_OPENMP_SUPPORT)
1013 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1020 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1022 PixelChannel channel = GetPixelChannelChannel(image,j);
1023 PixelTrait traits = GetPixelChannelTraits(image,channel);
1024 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1026 if (((traits & UpdatePixelTrait) == 0) ||
1027 ((reconstruct_traits & UpdatePixelTrait) == 0))
1029 similarity[j]+=channel_similarity[j];
1031 similarity[CompositePixelChannel]+=
1032 channel_similarity[CompositePixelChannel];
1033 mean_error+=channel_mean_error;
1034 if (channel_maximum_error > maximum_error)
1035 maximum_error=channel_maximum_error;
1038 reconstruct_view=DestroyCacheView(reconstruct_view);
1039 image_view=DestroyCacheView(image_view);
1040 area=MagickSafeReciprocal(area);
1041 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1043 PixelChannel channel = GetPixelChannelChannel(image,k);
1044 PixelTrait traits = GetPixelChannelTraits(image,channel);
1045 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1047 if (((traits & UpdatePixelTrait) == 0) ||
1048 ((reconstruct_traits & UpdatePixelTrait) == 0))
1050 similarity[k]*=area;
1052 similarity[CompositePixelChannel]*=area;
1053 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1054 image->error.mean_error_per_pixel=QuantumRange*
1055 similarity[CompositePixelChannel];
1056 image->error.normalized_mean_error=mean_error*area;
1057 image->error.normalized_maximum_error=maximum_error;
1061static MagickBooleanType GetMSESimilarity(
const Image *image,
1062 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1072 status = MagickTrue;
1085 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1086 image_view=AcquireVirtualCacheView(image,exception);
1087 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1088#if defined(MAGICKCORE_OPENMP_SUPPORT)
1089 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1090 magick_number_threads(image,image,rows,1)
1092 for (y=0; y < (ssize_t) rows; y++)
1100 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1105 if (status == MagickFalse)
1107 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1108 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1109 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1114 for (x=0; x < (ssize_t) columns; x++)
1123 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1124 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1126 p+=(ptrdiff_t) GetPixelChannels(image);
1127 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1130 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1131 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1132 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1137 PixelChannel channel = GetPixelChannelChannel(image,i);
1138 PixelTrait traits = GetPixelChannelTraits(image,channel);
1139 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1141 if (((traits & UpdatePixelTrait) == 0) ||
1142 ((reconstruct_traits & UpdatePixelTrait) == 0))
1144 if (channel == AlphaPixelChannel)
1145 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1146 reconstruct_image,channel,q));
1148 error=QuantumScale*(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1150 channel_similarity[i]+=error*error;
1151 channel_similarity[CompositePixelChannel]+=error*error;
1154 p+=(ptrdiff_t) GetPixelChannels(image);
1155 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1157#if defined(MAGICKCORE_OPENMP_SUPPORT)
1158 #pragma omp critical (MagickCore_GetMSESimilarity)
1165 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1167 PixelChannel channel = GetPixelChannelChannel(image,j);
1168 PixelTrait traits = GetPixelChannelTraits(image,channel);
1169 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1171 if (((traits & UpdatePixelTrait) == 0) ||
1172 ((reconstruct_traits & UpdatePixelTrait) == 0))
1174 similarity[j]+=channel_similarity[j];
1176 similarity[CompositePixelChannel]+=
1177 channel_similarity[CompositePixelChannel];
1180 reconstruct_view=DestroyCacheView(reconstruct_view);
1181 image_view=DestroyCacheView(image_view);
1182 area=MagickSafeReciprocal(area);
1183 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1185 PixelChannel channel = GetPixelChannelChannel(image,k);
1186 PixelTrait traits = GetPixelChannelTraits(image,channel);
1187 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1189 if (((traits & UpdatePixelTrait) == 0) ||
1190 ((reconstruct_traits & UpdatePixelTrait) == 0))
1192 similarity[k]*=area;
1194 similarity[CompositePixelChannel]*=area;
1195 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1199static MagickBooleanType GetNCCSimilarity(
const Image *image,
1200 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1208 *reconstruct_statistics;
1211 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1212 variance[MaxPixelChannels+1] = { 0.0 };
1215 status = MagickTrue;
1231 image_statistics=GetImageStatistics(image,exception);
1232 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1233 if ((image_statistics == (ChannelStatistics *) NULL) ||
1234 (reconstruct_statistics == (ChannelStatistics *) NULL))
1236 if (image_statistics != (ChannelStatistics *) NULL)
1237 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1239 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1240 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1241 reconstruct_statistics);
1242 return(MagickFalse);
1244 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1245 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1246 image_view=AcquireVirtualCacheView(image,exception);
1247 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1248#if defined(MAGICKCORE_OPENMP_SUPPORT)
1249 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1250 magick_number_threads(image,image,rows,1)
1252 for (y=0; y < (ssize_t) rows; y++)
1259 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1260 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1261 channel_variance[MaxPixelChannels+1] = { 0.0 };
1266 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1267 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1268 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1273 for (x=0; x < (ssize_t) columns; x++)
1282 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1283 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1285 p+=(ptrdiff_t) GetPixelChannels(image);
1286 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1289 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1290 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1291 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1297 PixelChannel channel = GetPixelChannelChannel(image,i);
1298 PixelTrait traits = GetPixelChannelTraits(image,channel);
1299 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1301 if (((traits & UpdatePixelTrait) == 0) ||
1302 ((reconstruct_traits & UpdatePixelTrait) == 0))
1304 if (channel == AlphaPixelChannel)
1306 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1307 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1308 channel,q)-reconstruct_statistics[channel].mean);
1312 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
1313 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
1314 q)-reconstruct_statistics[channel].mean);
1316 channel_similarity[i]+=alpha*beta;
1317 channel_variance[i]+=alpha*alpha;
1318 channel_reconstruct_variance[i]+=beta*beta;
1320 p+=(ptrdiff_t) GetPixelChannels(image);
1321 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1323#if defined(MAGICKCORE_OPENMP_SUPPORT)
1324 #pragma omp critical (MagickCore_GetNCCSimilarity)
1330 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1332 PixelChannel channel = GetPixelChannelChannel(image,j);
1333 PixelTrait traits = GetPixelChannelTraits(image,channel);
1334 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1336 if (((traits & UpdatePixelTrait) == 0) ||
1337 ((reconstruct_traits & UpdatePixelTrait) == 0))
1339 similarity[j]+=channel_similarity[j];
1340 similarity[CompositePixelChannel]+=channel_similarity[j];
1341 variance[j]+=channel_variance[j];
1342 variance[CompositePixelChannel]+=channel_variance[j];
1343 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1344 reconstruct_variance[CompositePixelChannel]+=
1345 channel_reconstruct_variance[j];
1348 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1353#if defined(MAGICKCORE_OPENMP_SUPPORT)
1357 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1358 if (proceed == MagickFalse)
1365 reconstruct_view=DestroyCacheView(reconstruct_view);
1366 image_view=DestroyCacheView(image_view);
1370 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1372 PixelChannel channel = GetPixelChannelChannel(image,k);
1373 PixelTrait traits = GetPixelChannelTraits(image,channel);
1374 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1376 if (((traits & UpdatePixelTrait) == 0) ||
1377 ((reconstruct_traits & UpdatePixelTrait) == 0))
1379 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1380 sqrt(reconstruct_variance[k]));
1382 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1383 variance[CompositePixelChannel])*sqrt(
1384 reconstruct_variance[CompositePixelChannel]));
1388 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1389 reconstruct_statistics);
1390 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1395static MagickBooleanType GetPASimilarity(
const Image *image,
1396 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1403 status = MagickTrue;
1415 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1416 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1417 image_view=AcquireVirtualCacheView(image,exception);
1418 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1419#if defined(MAGICKCORE_OPENMP_SUPPORT)
1420 #pragma omp parallel for schedule(static) shared(similarity,status) \
1421 magick_number_threads(image,image,rows,1)
1423 for (y=0; y < (ssize_t) rows; y++)
1430 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1435 if (status == MagickFalse)
1437 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1438 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1439 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1444 for (x=0; x < (ssize_t) columns; x++)
1453 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1454 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1456 p+=(ptrdiff_t) GetPixelChannels(image);
1457 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1460 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1461 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1462 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1467 PixelChannel channel = GetPixelChannelChannel(image,i);
1468 PixelTrait traits = GetPixelChannelTraits(image,channel);
1469 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1471 if (((traits & UpdatePixelTrait) == 0) ||
1472 ((reconstruct_traits & UpdatePixelTrait) == 0))
1474 if (channel == AlphaPixelChannel)
1475 distance=QuantumScale*fabs((
double) p[i]-(
double)
1476 GetPixelChannel(reconstruct_image,channel,q));
1478 distance=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(
1479 reconstruct_image,channel,q));
1480 if (distance > channel_similarity[i])
1481 channel_similarity[i]=distance;
1482 if (distance > channel_similarity[CompositePixelChannel])
1483 channel_similarity[CompositePixelChannel]=distance;
1485 p+=(ptrdiff_t) GetPixelChannels(image);
1486 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1488#if defined(MAGICKCORE_OPENMP_SUPPORT)
1489 #pragma omp critical (MagickCore_GetPASimilarity)
1495 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1497 PixelChannel channel = GetPixelChannelChannel(image,j);
1498 PixelTrait traits = GetPixelChannelTraits(image,channel);
1499 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1501 if (((traits & UpdatePixelTrait) == 0) ||
1502 ((reconstruct_traits & UpdatePixelTrait) == 0))
1504 if (channel_similarity[j] > similarity[j])
1505 similarity[j]=channel_similarity[j];
1507 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1508 similarity[CompositePixelChannel]=
1509 channel_similarity[CompositePixelChannel];
1512 reconstruct_view=DestroyCacheView(reconstruct_view);
1513 image_view=DestroyCacheView(image_view);
1517static MagickBooleanType GetPDCSimilarity(
const Image *image,
1518 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1529 status = MagickTrue;
1542 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1543 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1544 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1545 image_view=AcquireVirtualCacheView(image,exception);
1546 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1547#if defined(MAGICKCORE_OPENMP_SUPPORT)
1548 #pragma omp parallel for schedule(static) shared(similarity,status) \
1549 magick_number_threads(image,image,rows,1)
1551 for (y=0; y < (ssize_t) rows; y++)
1558 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1563 if (status == MagickFalse)
1565 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1566 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1567 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1572 for (x=0; x < (ssize_t) columns; x++)
1584 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1585 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1587 p+=(ptrdiff_t) GetPixelChannels(image);
1588 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1591 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1592 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1593 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1598 PixelChannel channel = GetPixelChannelChannel(image,i);
1599 PixelTrait traits = GetPixelChannelTraits(image,channel);
1600 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1602 if (((traits & UpdatePixelTrait) == 0) ||
1603 ((reconstruct_traits & UpdatePixelTrait) == 0))
1605 if (channel == AlphaPixelChannel)
1606 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1609 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1610 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1612 channel_similarity[i]++;
1617 channel_similarity[CompositePixelChannel]++;
1618 p+=(ptrdiff_t) GetPixelChannels(image);
1619 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1621#if defined(MAGICKCORE_OPENMP_SUPPORT)
1622 #pragma omp critical (MagickCore_GetPDCSimilarity)
1628 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1630 PixelChannel channel = GetPixelChannelChannel(image,j);
1631 PixelTrait traits = GetPixelChannelTraits(image,channel);
1632 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1634 if (((traits & UpdatePixelTrait) == 0) ||
1635 ((reconstruct_traits & UpdatePixelTrait) == 0))
1637 similarity[j]+=channel_similarity[j];
1639 similarity[CompositePixelChannel]+=
1640 channel_similarity[CompositePixelChannel];
1643 reconstruct_view=DestroyCacheView(reconstruct_view);
1644 image_view=DestroyCacheView(image_view);
1645 area=MagickSafeReciprocal((
double) columns*rows);
1646 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1647 similarity[k]*=area;
1648 similarity[CompositePixelChannel]*=area;
1652static MagickBooleanType DFTPhaseSpectrum(
const Image *image,
const ssize_t u,
1653 const ssize_t v,
double *phase,ExceptionInfo *exception)
1655#define PhaseImageTag "Phase/Image"
1661 channel_imag[MaxPixelChannels+1] = { 0.0 },
1662 channel_real[MaxPixelChannels+1] = { 0.0 };
1675 image_view=AcquireVirtualCacheView(image,exception);
1676 for (y=0; y < (ssize_t) image->rows; y++)
1684 if (status == MagickFalse)
1686 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1687 if (p == (
const Quantum *) NULL)
1692 for (x=0; x < (ssize_t) image->columns; x++)
1701 angle=MagickPI*((u*x/(double) image->rows)+(v*y/(double) image->columns));
1702 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1703 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1705 PixelChannel channel = GetPixelChannelChannel(image,i);
1706 PixelTrait traits = GetPixelChannelTraits(image,channel);
1707 if (traits == UndefinedPixelTrait)
1709 if (channel == AlphaPixelChannel)
1711 channel_real[i]+=(QuantumScale*p[i])*cos(angle);
1712 channel_imag[i]-=(QuantumScale*p[i])*sin(angle);
1716 channel_real[i]+=(QuantumScale*Sa*p[i])*cos(angle);
1717 channel_imag[i]-=(QuantumScale*Sa*p[i])*sin(angle);
1720 p+=(ptrdiff_t) GetPixelChannels(image);
1723 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1724 phase[k]=atan2(channel_imag[k],channel_real[k]);
1725 phase[CompositePixelChannel]=atan2(channel_imag[CompositePixelChannel],
1726 channel_real[CompositePixelChannel]);
1727 image_view=DestroyCacheView(image_view);
1731static MagickBooleanType GetPHASESimilarity(
const Image *image,
1732 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1742 status = MagickTrue;
1755 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1756 image_view=AcquireVirtualCacheView(image,exception);
1757 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1758#if defined(MAGICKCORE_OPENMP_SUPPORT)
1759 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1760 magick_number_threads(image,image,rows,1)
1762 for (y=0; y < (ssize_t) rows; y++)
1770 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1775 if (status == MagickFalse)
1777 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1778 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1779 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1784 for (x=0; x < (ssize_t) columns; x++)
1787 phase[MaxPixelChannels+1] = { 0.0 },
1788 reconstruct_phase[MaxPixelChannels+1] = { 0.0 };
1793 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1794 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1796 p+=(ptrdiff_t) GetPixelChannels(image);
1797 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1800 status=DFTPhaseSpectrum(image,x,y,phase,exception);
1801 if (status == MagickFalse)
1803 status=DFTPhaseSpectrum(reconstruct_image,x,y,reconstruct_phase,
1805 if (status == MagickFalse)
1807 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1812 PixelChannel channel = GetPixelChannelChannel(image,i);
1813 PixelTrait traits = GetPixelChannelTraits(image,channel);
1814 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1816 if (((traits & UpdatePixelTrait) == 0) ||
1817 ((reconstruct_traits & UpdatePixelTrait) == 0))
1819 delta=phase[i]-reconstruct_phase[i];
1820 channel_similarity[i]+=cos(delta);
1821 channel_similarity[CompositePixelChannel]+=cos(delta);
1824 p+=(ptrdiff_t) GetPixelChannels(image);
1825 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1827#if defined(MAGICKCORE_OPENMP_SUPPORT)
1828 #pragma omp critical (MagickCore_GetPHASESimilarity)
1835 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1837 PixelChannel channel = GetPixelChannelChannel(image,j);
1838 PixelTrait traits = GetPixelChannelTraits(image,channel);
1839 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1841 if (((traits & UpdatePixelTrait) == 0) ||
1842 ((reconstruct_traits & UpdatePixelTrait) == 0))
1844 similarity[j]+=channel_similarity[j];
1846 similarity[CompositePixelChannel]+=
1847 channel_similarity[CompositePixelChannel];
1850 reconstruct_view=DestroyCacheView(reconstruct_view);
1851 image_view=DestroyCacheView(image_view);
1852 area=MagickSafeReciprocal(area);
1853 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1855 PixelChannel channel = GetPixelChannelChannel(image,k);
1856 PixelTrait traits = GetPixelChannelTraits(image,channel);
1857 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1859 if (((traits & UpdatePixelTrait) == 0) ||
1860 ((reconstruct_traits & UpdatePixelTrait) == 0))
1862 similarity[k]*=area;
1864 similarity[CompositePixelChannel]*=area;
1865 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1869static MagickBooleanType GetPSNRSimilarity(
const Image *image,
1870 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1873 status = MagickTrue;
1881 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
1882 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1884 PixelChannel channel = GetPixelChannelChannel(image,i);
1885 PixelTrait traits = GetPixelChannelTraits(image,channel);
1886 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1888 if (((traits & UpdatePixelTrait) == 0) ||
1889 ((reconstruct_traits & UpdatePixelTrait) == 0))
1891 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
1892 similarity[i]))/MagickSafePSNRRecipicol(10.0);
1894 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
1895 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
1896 MagickSafePSNRRecipicol(10.0);
1900static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1901 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1903 ChannelPerceptualHash
1916 channel_phash=GetImagePerceptualHash(image,exception);
1917 if (channel_phash == (ChannelPerceptualHash *) NULL)
1918 return(MagickFalse);
1919 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1920 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1922 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1924 return(MagickFalse);
1926 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1934 PixelChannel channel = GetPixelChannelChannel(image,i);
1935 PixelTrait traits = GetPixelChannelTraits(image,channel);
1936 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1938 if (((traits & UpdatePixelTrait) == 0) ||
1939 ((reconstruct_traits & UpdatePixelTrait) == 0))
1941 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1950 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1955 alpha=channel_phash[i].phash[j][k];
1956 beta=reconstruct_phash[i].phash[j][k];
1958 if (IsNaN(error) != 0)
1960 difference+=error*error;
1963 similarity[i]+=difference;
1964 similarity[CompositePixelChannel]+=difference;
1966 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1967 artifact=GetImageArtifact(image,
"phash:normalize");
1968 if (IsStringTrue(artifact) != MagickFalse)
1970 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1972 PixelChannel channel = GetPixelChannelChannel(image,i);
1973 PixelTrait traits = GetPixelChannelTraits(image,channel);
1974 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1976 if (((traits & UpdatePixelTrait) == 0) ||
1977 ((reconstruct_traits & UpdatePixelTrait) == 0))
1979 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
1981 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
1982 channel_phash[0].number_colorspaces);
1987 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1989 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
1993static MagickBooleanType GetRMSESimilarity(
const Image *image,
1994 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1996#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
1999 status = MagickTrue;
2007 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2008 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2010 PixelChannel channel = GetPixelChannelChannel(image,i);
2011 PixelTrait traits = GetPixelChannelTraits(image,channel);
2012 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2014 if (((traits & UpdatePixelTrait) == 0) ||
2015 ((reconstruct_traits & UpdatePixelTrait) == 0))
2017 similarity[i]=RMSESquareRoot(similarity[i]);
2019 similarity[CompositePixelChannel]=RMSESquareRoot(
2020 similarity[CompositePixelChannel]);
2024static MagickBooleanType GetSSIMSimularity(
const Image *image,
2025 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2027#define SSIMRadius 5.0
2028#define SSIMSigma 1.5
2038 geometry[MagickPathExtent];
2054 status = MagickTrue;
2068 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2069 if (artifact != (
const char *) NULL)
2070 radius=StringToDouble(artifact,(
char **) NULL);
2072 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2073 if (artifact != (
const char *) NULL)
2074 sigma=StringToDouble(artifact,(
char **) NULL);
2075 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.20gx%.20g",
2077 kernel_info=AcquireKernelInfo(geometry,exception);
2078 if (kernel_info == (KernelInfo *) NULL)
2079 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2081 c1=pow(SSIMK1*SSIML,2.0);
2082 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2083 if (artifact != (
const char *) NULL)
2084 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2085 c2=pow(SSIMK2*SSIML,2.0);
2086 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2087 if (artifact != (
const char *) NULL)
2088 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2089 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2090 image_view=AcquireVirtualCacheView(image,exception);
2091 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2092#if defined(MAGICKCORE_OPENMP_SUPPORT)
2093 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2094 magick_number_threads(image,reconstruct_image,rows,1)
2096 for (y=0; y < (ssize_t) rows; y++)
2104 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2110 if (status == MagickFalse)
2112 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2113 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2114 kernel_info->height,exception);
2115 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2116 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2117 kernel_info->height,exception);
2118 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2123 for (x=0; x < (ssize_t) columns; x++)
2126 *magick_restrict reconstruct,
2127 *magick_restrict test;
2130 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2131 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2132 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2133 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2134 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2142 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2143 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2145 p+=(ptrdiff_t) GetPixelChannels(image);
2146 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2149 k=kernel_info->values;
2152 for (v=0; v < (ssize_t) kernel_info->height; v++)
2157 for (u=0; u < (ssize_t) kernel_info->width; u++)
2159 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2165 PixelChannel channel = GetPixelChannelChannel(image,i);
2166 PixelTrait traits = GetPixelChannelTraits(image,channel);
2167 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2168 reconstruct_image,channel);
2169 if (((traits & UpdatePixelTrait) == 0) ||
2170 ((reconstruct_traits & UpdatePixelTrait) == 0))
2172 x_pixel=QuantumScale*(double) test[i];
2173 x_pixel_mu[i]+=(*k)*x_pixel;
2174 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2175 y_pixel=QuantumScale*(double)
2176 GetPixelChannel(reconstruct_image,channel,reconstruct);
2177 y_pixel_mu[i]+=(*k)*y_pixel;
2178 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2179 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2182 test+=(ptrdiff_t) GetPixelChannels(image);
2183 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2185 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2186 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2188 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2193 x_pixel_sigmas_squared,
2197 y_pixel_sigmas_squared;
2199 PixelChannel channel = GetPixelChannelChannel(image,i);
2200 PixelTrait traits = GetPixelChannelTraits(image,channel);
2201 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2202 reconstruct_image,channel);
2203 if (((traits & UpdatePixelTrait) == 0) ||
2204 ((reconstruct_traits & UpdatePixelTrait) == 0))
2206 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2207 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2208 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2209 xy_sigmas=xy_sigma[i]-xy_mu;
2210 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2211 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2212 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2213 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2214 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2215 channel_similarity[i]+=ssim;
2216 channel_similarity[CompositePixelChannel]+=ssim;
2218 p+=(ptrdiff_t) GetPixelChannels(image);
2219 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2222#if defined(MAGICKCORE_OPENMP_SUPPORT)
2223 #pragma omp critical (MagickCore_GetSSIMSimularity)
2230 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2232 PixelChannel channel = GetPixelChannelChannel(image,j);
2233 PixelTrait traits = GetPixelChannelTraits(image,channel);
2234 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2236 if (((traits & UpdatePixelTrait) == 0) ||
2237 ((reconstruct_traits & UpdatePixelTrait) == 0))
2239 similarity[j]+=channel_similarity[j];
2241 similarity[CompositePixelChannel]+=
2242 channel_similarity[CompositePixelChannel];
2245 image_view=DestroyCacheView(image_view);
2246 reconstruct_view=DestroyCacheView(reconstruct_view);
2247 area=MagickSafeReciprocal(area);
2248 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2250 PixelChannel channel = GetPixelChannelChannel(image,l);
2251 PixelTrait traits = GetPixelChannelTraits(image,channel);
2252 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2254 if (((traits & UpdatePixelTrait) == 0) ||
2255 ((reconstruct_traits & UpdatePixelTrait) == 0))
2257 similarity[l]*=area;
2259 similarity[CompositePixelChannel]*=area;
2260 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2261 kernel_info=DestroyKernelInfo(kernel_info);
2265static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2266 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2269 status = MagickTrue;
2277 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2278 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2280 PixelChannel channel = GetPixelChannelChannel(image,i);
2281 PixelTrait traits = GetPixelChannelTraits(image,channel);
2282 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2284 if (((traits & UpdatePixelTrait) == 0) ||
2285 ((reconstruct_traits & UpdatePixelTrait) == 0))
2287 similarity[i]=(1.0-similarity[i])/2.0;
2289 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2293MagickExport MagickBooleanType GetImageDistortion(Image *image,
2294 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2295 ExceptionInfo *exception)
2297#define CompareMetricNotSupportedException "metric not supported"
2300 *channel_similarity;
2303 status = MagickTrue;
2308 assert(image != (Image *) NULL);
2309 assert(image->signature == MagickCoreSignature);
2310 assert(reconstruct_image != (
const Image *) NULL);
2311 assert(reconstruct_image->signature == MagickCoreSignature);
2312 assert(distortion != (
double *) NULL);
2313 if (IsEventLogging() != MagickFalse)
2314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2319 length=MaxPixelChannels+1UL;
2320 channel_similarity=(
double *) AcquireQuantumMemory(length,
2321 sizeof(*channel_similarity));
2322 if (channel_similarity == (
double *) NULL)
2323 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2324 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2327 case AbsoluteErrorMetric:
2329 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2333 case DotProductCorrelationErrorMetric:
2335 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2339 case FuzzErrorMetric:
2341 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2345 case MeanAbsoluteErrorMetric:
2347 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2351 case MeanErrorPerPixelErrorMetric:
2353 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2357 case MeanSquaredErrorMetric:
2359 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2363 case NormalizedCrossCorrelationErrorMetric:
2365 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2369 case PeakAbsoluteErrorMetric:
2371 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2375 case PeakSignalToNoiseRatioErrorMetric:
2377 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2381 case PerceptualHashErrorMetric:
2383 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2387 case PhaseCorrelationErrorMetric:
2389 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2393 case PixelDifferenceCountErrorMetric:
2395 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2399 case RootMeanSquaredErrorMetric:
2400 case UndefinedErrorMetric:
2403 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2407 case StructuralDissimilarityErrorMetric:
2409 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2413 case StructuralSimilarityErrorMetric:
2415 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2420 *distortion=channel_similarity[CompositePixelChannel];
2423 case DotProductCorrelationErrorMetric:
2424 case NormalizedCrossCorrelationErrorMetric:
2425 case PhaseCorrelationErrorMetric:
2426 case StructuralSimilarityErrorMetric:
2428 *distortion=(1.0-(*distortion))/2.0;
2433 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2434 if (fabs(*distortion) < MagickEpsilon)
2436 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2472MagickExport
double *GetImageDistortions(Image *image,
2473 const Image *reconstruct_image,
const MetricType metric,
2474 ExceptionInfo *exception)
2478 *channel_similarity;
2481 status = MagickTrue;
2489 assert(image != (Image *) NULL);
2490 assert(image->signature == MagickCoreSignature);
2491 assert(reconstruct_image != (
const Image *) NULL);
2492 assert(reconstruct_image->signature == MagickCoreSignature);
2493 if (IsEventLogging() != MagickFalse)
2494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2498 length=MaxPixelChannels+1UL;
2499 channel_similarity=(
double *) AcquireQuantumMemory(length,
2500 sizeof(*channel_similarity));
2501 if (channel_similarity == (
double *) NULL)
2502 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2503 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2506 case AbsoluteErrorMetric:
2508 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2512 case DotProductCorrelationErrorMetric:
2514 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2518 case FuzzErrorMetric:
2520 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2524 case MeanAbsoluteErrorMetric:
2526 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2530 case MeanErrorPerPixelErrorMetric:
2532 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2536 case MeanSquaredErrorMetric:
2538 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2542 case NormalizedCrossCorrelationErrorMetric:
2544 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2548 case PeakAbsoluteErrorMetric:
2550 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2554 case PeakSignalToNoiseRatioErrorMetric:
2556 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2560 case PerceptualHashErrorMetric:
2562 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2566 case PhaseCorrelationErrorMetric:
2568 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2572 case PixelDifferenceCountErrorMetric:
2574 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2578 case RootMeanSquaredErrorMetric:
2579 case UndefinedErrorMetric:
2582 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2586 case StructuralDissimilarityErrorMetric:
2588 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2592 case StructuralSimilarityErrorMetric:
2594 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2599 if (status == MagickFalse)
2601 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2602 return((
double *) NULL);
2604 distortion=channel_similarity;
2607 case DotProductCorrelationErrorMetric:
2608 case NormalizedCrossCorrelationErrorMetric:
2609 case PhaseCorrelationErrorMetric:
2610 case StructuralSimilarityErrorMetric:
2612 for (i=0; i <= MaxPixelChannels; i++)
2613 distortion[i]=(1.0-distortion[i])/2.0;
2618 for (i=0; i <= MaxPixelChannels; i++)
2619 if (fabs(distortion[i]) < MagickEpsilon)
2621 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2622 distortion[CompositePixelChannel]);
2654MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2655 const Image *reconstruct_image,ExceptionInfo *exception)
2668 assert(image != (Image *) NULL);
2669 assert(image->signature == MagickCoreSignature);
2670 assert(reconstruct_image != (
const Image *) NULL);
2671 assert(reconstruct_image->signature == MagickCoreSignature);
2672 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2673 image_view=AcquireVirtualCacheView(image,exception);
2674 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2675 for (y=0; y < (ssize_t) rows; y++)
2684 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2685 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2686 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2688 for (x=0; x < (ssize_t) columns; x++)
2693 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2698 PixelChannel channel = GetPixelChannelChannel(image,i);
2699 PixelTrait traits = GetPixelChannelTraits(image,channel);
2700 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2702 if (((traits & UpdatePixelTrait) == 0) ||
2703 ((reconstruct_traits & UpdatePixelTrait) == 0))
2705 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2707 if (distance >= MagickEpsilon)
2710 if (i < (ssize_t) GetPixelChannels(image))
2712 p+=(ptrdiff_t) GetPixelChannels(image);
2713 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2715 if (x < (ssize_t) columns)
2718 reconstruct_view=DestroyCacheView(reconstruct_view);
2719 image_view=DestroyCacheView(image_view);
2720 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2772MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2773 const Image *reconstruct_image,ExceptionInfo *exception)
2776 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2781 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2783 if (status == MagickFalse)
2784 return(MagickFalse);
2785 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2786 MagickTrue : MagickFalse;
2833#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2834static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2835 const Image *beta_image,ExceptionInfo *exception)
2838 *alpha_fft = (Image *) NULL,
2839 *beta_fft = (Image *) NULL,
2840 *complex_conjugate = (Image *) NULL,
2841 *complex_multiplication = (Image *) NULL,
2842 *cross_correlation = (Image *) NULL,
2843 *temp_image = (Image *) NULL;
2848 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2849 if (temp_image == (Image *) NULL)
2850 return((Image *) NULL);
2851 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2852 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2853 temp_image=DestroyImageList(temp_image);
2854 if (beta_fft == (Image *) NULL)
2855 return((Image *) NULL);
2859 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2860 beta_fft=DestroyImageList(beta_fft);
2861 if (complex_conjugate == (Image *) NULL)
2862 return((Image *) NULL);
2866 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2867 if (temp_image == (Image *) NULL)
2869 complex_conjugate=DestroyImageList(complex_conjugate);
2870 return((Image *) NULL);
2872 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2873 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2874 temp_image=DestroyImageList(temp_image);
2875 if (alpha_fft == (Image *) NULL)
2877 complex_conjugate=DestroyImageList(complex_conjugate);
2878 return((Image *) NULL);
2883 DisableCompositeClampUnlessSpecified(complex_conjugate);
2884 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2885 AppendImageToList(&complex_conjugate,alpha_fft);
2886 complex_multiplication=ComplexImages(complex_conjugate,
2887 MultiplyComplexOperator,exception);
2888 complex_conjugate=DestroyImageList(complex_conjugate);
2889 if (complex_multiplication == (Image *) NULL)
2890 return((Image *) NULL);
2894 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2895 complex_multiplication->next,MagickFalse,exception);
2896 complex_multiplication=DestroyImageList(complex_multiplication);
2897 return(cross_correlation);
2900static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2901 ExceptionInfo *exception)
2909 kernel_info=AcquireKernelInfo(kernel,exception);
2910 if (kernel_info == (KernelInfo *) NULL)
2911 return((Image *) NULL);
2912 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2914 kernel_info=DestroyKernelInfo(kernel_info);
2915 return(derivative_image);
2918static Image *SIMDivideImage(
const Image *numerator_image,
2919 const Image *denominator_image,ExceptionInfo *exception)
2929 status = MagickTrue;
2937 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2938 if (divide_image == (Image *) NULL)
2939 return(divide_image);
2940 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2941 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2942#if defined(MAGICKCORE_OPENMP_SUPPORT)
2943 #pragma omp parallel for schedule(static) shared(status) \
2944 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
2946 for (y=0; y < (ssize_t) divide_image->rows; y++)
2957 if (status == MagickFalse)
2959 p=GetCacheViewVirtualPixels(denominator_view,0,y,
2960 denominator_image->columns,1,exception);
2961 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
2963 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2968 for (x=0; x < (ssize_t) divide_image->columns; x++)
2973 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
2975 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
2976 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
2977 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
2979 if (((traits & UpdatePixelTrait) == 0) ||
2980 ((denominator_traits & UpdatePixelTrait) == 0))
2982 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
2983 (
double) GetPixelChannel(denominator_image,channel,p)));
2985 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
2986 q+=(ptrdiff_t) GetPixelChannels(divide_image);
2988 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
2991 denominator_view=DestroyCacheView(denominator_view);
2992 numerator_view=DestroyCacheView(numerator_view);
2993 if (status == MagickFalse)
2994 divide_image=DestroyImage(divide_image);
2995 return(divide_image);
2998static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
2999 const Image *source_image,ExceptionInfo *exception)
3008 divide_image=SIMDivideImage(image,magnitude_image,exception);
3009 if (divide_image == (Image *) NULL)
3010 return((Image *) NULL);
3011 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3012 ÷_image->background_color);
3013 SetGeometry(source_image,&geometry);
3014 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3015 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3016 result_image=ExtentImage(divide_image,&geometry,exception);
3017 divide_image=DestroyImage(divide_image);
3018 return(result_image);
3021static MagickBooleanType SIMFilterImageNaNs(Image *image,
3022 ExceptionInfo *exception)
3028 status = MagickTrue;
3036 image_view=AcquireAuthenticCacheView(image,exception);
3037#if defined(MAGICKCORE_OPENMP_SUPPORT)
3038 #pragma omp parallel for schedule(static) shared(status) \
3039 magick_number_threads(image,image,image->rows,1)
3041 for (y=0; y < (ssize_t) image->rows; y++)
3049 if (status == MagickFalse)
3051 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3052 if (q == (Quantum *) NULL)
3057 for (x=0; x < (ssize_t) image->columns; x++)
3062 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3064 PixelChannel channel = GetPixelChannelChannel(image,i);
3065 PixelTrait traits = GetPixelChannelTraits(image,channel);
3066 if ((traits & UpdatePixelTrait) == 0)
3068 if (IsNaN((
double) q[i]) != 0)
3071 q+=(ptrdiff_t) GetPixelChannels(image);
3073 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3076 image_view=DestroyCacheView(image_view);
3080static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3089 status = MagickTrue;
3097 square_image=CloneImage(image,0,0,MagickTrue,exception);
3098 if (square_image == (Image *) NULL)
3099 return(square_image);
3100 image_view=AcquireAuthenticCacheView(square_image,exception);
3101#if defined(MAGICKCORE_OPENMP_SUPPORT)
3102 #pragma omp parallel for schedule(static) shared(status) \
3103 magick_number_threads(square_image,square_image,square_image->rows,1)
3105 for (y=0; y < (ssize_t) square_image->rows; y++)
3113 if (status == MagickFalse)
3115 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3117 if (q == (Quantum *) NULL)
3122 for (x=0; x < (ssize_t) square_image->columns; x++)
3127 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3129 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3130 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3131 if ((traits & UpdatePixelTrait) == 0)
3133 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3135 q+=(ptrdiff_t) GetPixelChannels(square_image);
3137 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3140 image_view=DestroyCacheView(image_view);
3141 if (status == MagickFalse)
3142 square_image=DestroyImage(square_image);
3143 return(square_image);
3146static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3147 ExceptionInfo *exception)
3155 status = MagickTrue;
3157 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3158 xsq_image=SIMSquareImage(alpha_image,exception);
3159 if (xsq_image == (Image *) NULL)
3160 return((Image *) NULL);
3161 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3162 ysq_image=SIMSquareImage(beta_image,exception);
3163 if (ysq_image == (Image *) NULL)
3165 xsq_image=DestroyImage(xsq_image);
3166 return((Image *) NULL);
3168 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3170 magnitude_image=xsq_image;
3171 ysq_image=DestroyImage(ysq_image);
3172 if (status == MagickFalse)
3174 magnitude_image=DestroyImage(magnitude_image);
3175 return((Image *) NULL);
3177 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3178 if (status == MagickFalse)
3180 magnitude_image=DestroyImage(magnitude_image);
3181 return (Image *) NULL;
3183 return(magnitude_image);
3186static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3187 RectangleInfo *offset,ExceptionInfo *exception)
3206 status = MagickTrue;
3209 maxima_info = { -MagickMaximumValue, 0, 0 };
3217 image_view=AcquireVirtualCacheView(image,exception);
3218 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3220 if (q != (
const Quantum *) NULL)
3221 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3222#if defined(MAGICKCORE_OPENMP_SUPPORT)
3223 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3224 magick_number_threads(image,image,image->rows,1)
3226 for (y=0; y < (ssize_t) image->rows; y++)
3232 channel_maxima = { -MagickMaximumValue, 0, 0 };
3237 if (status == MagickFalse)
3239 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3240 if (p == (
const Quantum *) NULL)
3245 channel_maxima=maxima_info;
3246 for (x=0; x < (ssize_t) image->columns; x++)
3251 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3256 PixelChannel channel = GetPixelChannelChannel(image,i);
3257 PixelTrait traits = GetPixelChannelTraits(image,channel);
3258 if ((traits & UpdatePixelTrait) == 0)
3260 pixel=(double) p[i];
3261 if (IsNaN(pixel) != 0)
3263 if (pixel > channel_maxima.maxima)
3265 channel_maxima.maxima=(double) p[i];
3270 p+=(ptrdiff_t) GetPixelChannels(image);
3272#if defined(MAGICKCORE_OPENMP_SUPPORT)
3273 #pragma omp critical (MagickCore_SIMMaximaImage)
3275 if (channel_maxima.maxima > maxima_info.maxima)
3276 maxima_info=channel_maxima;
3278 image_view=DestroyCacheView(image_view);
3279 *maxima=maxima_info.maxima;
3280 offset->x=maxima_info.x;
3281 offset->y=maxima_info.y;
3285static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3286 RectangleInfo *offset,ExceptionInfo *exception)
3305 status = MagickTrue;
3308 minima_info = { MagickMaximumValue, 0, 0 };
3316 image_view=AcquireVirtualCacheView(image,exception);
3317 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3319 if (q != (
const Quantum *) NULL)
3320 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3321#if defined(MAGICKCORE_OPENMP_SUPPORT)
3322 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3323 magick_number_threads(image,image,image->rows,1)
3325 for (y=0; y < (ssize_t) image->rows; y++)
3331 channel_minima = { MagickMaximumValue, 0, 0 };
3336 if (status == MagickFalse)
3338 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3339 if (p == (
const Quantum *) NULL)
3344 channel_minima=minima_info;
3345 for (x=0; x < (ssize_t) image->columns; x++)
3350 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3355 PixelChannel channel = GetPixelChannelChannel(image,i);
3356 PixelTrait traits = GetPixelChannelTraits(image,channel);
3357 if ((traits & UpdatePixelTrait) == 0)
3359 pixel=(double) p[i];
3360 if (IsNaN(pixel) != 0)
3362 if (pixel < channel_minima.minima)
3364 channel_minima.minima=pixel;
3369 p+=(ptrdiff_t) GetPixelChannels(image);
3371#if defined(MAGICKCORE_OPENMP_SUPPORT)
3372 #pragma omp critical (MagickCore_SIMMinimaImage)
3374 if (channel_minima.minima < minima_info.minima)
3375 minima_info=channel_minima;
3377 image_view=DestroyCacheView(image_view);
3378 *minima=minima_info.minima;
3379 offset->x=minima_info.x;
3380 offset->y=minima_info.y;
3384static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3385 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3391 status = MagickTrue;
3399 image_view=AcquireAuthenticCacheView(image,exception);
3400#if defined(MAGICKCORE_OPENMP_SUPPORT)
3401 #pragma omp parallel for schedule(static) shared(status) \
3402 magick_number_threads(image,image,image->rows,1)
3404 for (y=0; y < (ssize_t) image->rows; y++)
3412 if (status == MagickFalse)
3414 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3415 if (q == (Quantum *) NULL)
3420 for (x=0; x < (ssize_t) image->columns; x++)
3425 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3427 PixelChannel channel = GetPixelChannelChannel(image,i);
3428 PixelTrait traits = GetPixelChannelTraits(image,channel);
3429 if ((traits & UpdatePixelTrait) == 0)
3431 if (channel_statistics != (
const ChannelStatistics *) NULL)
3432 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3433 channel_statistics[channel].standard_deviation);
3435 q[i]=(Quantum) (factor*q[i]);
3437 q+=(ptrdiff_t) GetPixelChannels(image);
3439 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3442 image_view=DestroyCacheView(image_view);
3446static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3447 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3450 *alpha_fft = (Image *) NULL,
3451 *beta_fft = (Image *) NULL,
3452 *complex_multiplication = (Image *) NULL,
3453 *cross_correlation = (Image *) NULL;
3458 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3459 if (beta_fft == NULL)
3460 return((Image *) NULL);
3461 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3462 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3463 if (beta_fft == NULL)
3464 return((Image *) NULL);
3468 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3469 if (alpha_fft == (Image *) NULL)
3471 beta_fft=DestroyImageList(beta_fft);
3472 return((Image *) NULL);
3474 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3475 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3476 if (alpha_fft == (Image *) NULL)
3478 beta_fft=DestroyImageList(beta_fft);
3479 return((Image *) NULL);
3484 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3485 if (beta_fft == (Image *) NULL)
3487 alpha_fft=DestroyImageList(alpha_fft);
3488 return((Image *) NULL);
3493 AppendImageToList(&beta_fft,alpha_fft);
3494 DisableCompositeClampUnlessSpecified(beta_fft);
3495 DisableCompositeClampUnlessSpecified(beta_fft->next);
3496 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3498 beta_fft=DestroyImageList(beta_fft);
3499 if (complex_multiplication == (Image *) NULL)
3500 return((Image *) NULL);
3504 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3505 magnitude_image,0,0,exception);
3509 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3510 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3511 complex_multiplication->next,MagickFalse,exception);
3512 complex_multiplication=DestroyImageList(complex_multiplication);
3513 return(cross_correlation);
3516static MagickBooleanType SIMSetImageMean(Image *image,
3517 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3523 status = MagickTrue;
3531 image_view=AcquireAuthenticCacheView(image,exception);
3532#if defined(MAGICKCORE_OPENMP_SUPPORT)
3533 #pragma omp parallel for schedule(static) shared(status) \
3534 magick_number_threads(image,image,image->rows,1)
3536 for (y=0; y < (ssize_t) image->rows; y++)
3544 if (status == MagickFalse)
3546 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3547 if (q == (Quantum *) NULL)
3552 for (x=0; x < (ssize_t) image->columns; x++)
3557 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3559 PixelChannel channel = GetPixelChannelChannel(image,i);
3560 PixelTrait traits = GetPixelChannelTraits(image,channel);
3561 if ((traits & UpdatePixelTrait) == 0)
3563 q[i]=(Quantum) channel_statistics[channel].mean;
3565 q+=(ptrdiff_t) GetPixelChannels(image);
3567 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3570 image_view=DestroyCacheView(image_view);
3574static Image *SIMSubtractImageMean(
const Image *alpha_image,
3575 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3576 ExceptionInfo *exception)
3586 status = MagickTrue;
3594 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3595 MagickTrue,exception);
3596 if (subtract_image == (Image *) NULL)
3597 return(subtract_image);
3598 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3599 beta_view=AcquireVirtualCacheView(beta_image,exception);
3600#if defined(MAGICKCORE_OPENMP_SUPPORT)
3601 #pragma omp parallel for schedule(static) shared(status) \
3602 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3604 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3615 if (status == MagickFalse)
3617 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3618 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3620 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3625 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3630 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3632 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3633 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3634 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3635 if (((traits & UpdatePixelTrait) == 0) ||
3636 ((beta_traits & UpdatePixelTrait) == 0))
3638 if ((x >= (ssize_t) beta_image->columns) ||
3639 (y >= (ssize_t) beta_image->rows))
3642 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3643 channel_statistics[channel].mean);
3645 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3646 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3648 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3651 beta_view=DestroyCacheView(beta_view);
3652 image_view=DestroyCacheView(image_view);
3653 if (status == MagickFalse)
3654 subtract_image=DestroyImage(subtract_image);
3655 return(subtract_image);
3658static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3659 ExceptionInfo *exception)
3668 status = MagickTrue;
3676 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3677 MagickTrue,exception);
3678 if (unity_image == (Image *) NULL)
3679 return(unity_image);
3680 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3681 return(DestroyImage(unity_image));
3682 image_view=AcquireAuthenticCacheView(unity_image,exception);
3683#if defined(MAGICKCORE_OPENMP_SUPPORT)
3684 #pragma omp parallel for schedule(static) shared(status) \
3685 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3687 for (y=0; y < (ssize_t) unity_image->rows; y++)
3695 if (status == MagickFalse)
3697 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3699 if (q == (Quantum *) NULL)
3704 for (x=0; x < (ssize_t) unity_image->columns; x++)
3709 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3711 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3712 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3713 if ((traits & UpdatePixelTrait) == 0)
3715 if ((x >= (ssize_t) beta_image->columns) ||
3716 (y >= (ssize_t) beta_image->rows))
3721 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3723 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3726 image_view=DestroyCacheView(image_view);
3727 if (status == MagickFalse)
3728 unity_image=DestroyImage(unity_image);
3729 return(unity_image);
3732static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3733 ExceptionInfo *exception)
3743 status = MagickTrue;
3751 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3752 if (variance_image == (Image *) NULL)
3753 return(variance_image);
3754 image_view=AcquireAuthenticCacheView(variance_image,exception);
3755 beta_view=AcquireVirtualCacheView(beta_image,exception);
3756#if defined(MAGICKCORE_OPENMP_SUPPORT)
3757 #pragma omp parallel for schedule(static) shared(status) \
3758 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3760 for (y=0; y < (ssize_t) variance_image->rows; y++)
3771 if (status == MagickFalse)
3773 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3775 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3777 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3782 for (x=0; x < (ssize_t) variance_image->columns; x++)
3787 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3792 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3793 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3794 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3795 if (((traits & UpdatePixelTrait) == 0) ||
3796 ((beta_traits & UpdatePixelTrait) == 0))
3798 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3799 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3800 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3802 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3803 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3805 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3808 beta_view=DestroyCacheView(beta_view);
3809 image_view=DestroyCacheView(image_view);
3810 if (status == MagickFalse)
3811 variance_image=DestroyImage(variance_image);
3812 return(variance_image);
3815static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3816 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3818#define ThrowDPCSimilarityException() \
3820 if (dot_product_image != (Image *) NULL) \
3821 dot_product_image=DestroyImage(dot_product_image); \
3822 if (magnitude_image != (Image *) NULL) \
3823 magnitude_image=DestroyImage(magnitude_image); \
3824 if (reconstruct_image != (Image *) NULL) \
3825 reconstruct_image=DestroyImage(reconstruct_image); \
3826 if (rx_image != (Image *) NULL) \
3827 rx_image=DestroyImage(rx_image); \
3828 if (ry_image != (Image *) NULL) \
3829 ry_image=DestroyImage(ry_image); \
3830 if (target_image != (Image *) NULL) \
3831 target_image=DestroyImage(target_image); \
3832 if (threshold_image != (Image *) NULL) \
3833 threshold_image=DestroyImage(threshold_image); \
3834 if (trx_image != (Image *) NULL) \
3835 trx_image=DestroyImage(trx_image); \
3836 if (try_image != (Image *) NULL) \
3837 try_image=DestroyImage(try_image); \
3838 if (tx_image != (Image *) NULL) \
3839 tx_image=DestroyImage(tx_image); \
3840 if (ty_image != (Image *) NULL) \
3841 ty_image=DestroyImage(ty_image); \
3842 return((Image *) NULL); \
3849 standard_deviation = 0.0;
3852 *dot_product_image = (Image *) NULL,
3853 *magnitude_image = (Image *) NULL,
3854 *reconstruct_image = (Image *) NULL,
3855 *rx_image = (Image *) NULL,
3856 *ry_image = (Image *) NULL,
3857 *trx_image = (Image *) NULL,
3858 *target_image = (Image *) NULL,
3859 *threshold_image = (Image *) NULL,
3860 *try_image = (Image *) NULL,
3861 *tx_image = (Image *) NULL,
3862 *ty_image = (Image *) NULL;
3865 status = MagickTrue;
3873 target_image=CloneImage(image,0,0,MagickTrue,exception);
3874 if (target_image == (Image *) NULL)
3875 return((Image *) NULL);
3879 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3880 if (reconstruct_image == (Image *) NULL)
3881 ThrowDPCSimilarityException();
3885 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3887 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3888 if (rx_image == (Image *) NULL)
3889 ThrowDPCSimilarityException();
3890 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3891 reconstruct_image=DestroyImage(reconstruct_image);
3892 if (ry_image == (Image *) NULL)
3893 ThrowDPCSimilarityException();
3897 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3898 if (magnitude_image == (Image *) NULL)
3899 ThrowDPCSimilarityException();
3903 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3904 if (threshold_image == (Image *) NULL)
3905 ThrowDPCSimilarityException();
3906 status=BilevelImage(threshold_image,0.0,exception);
3907 if (status == MagickFalse)
3908 ThrowDPCSimilarityException();
3909 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3910 threshold_image=DestroyImage(threshold_image);
3911 if (status == MagickFalse)
3912 ThrowDPCSimilarityException();
3913 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
3914 reconstruct->rows)+QuantumScale;
3918 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3919 rx_image=DestroyImage(rx_image);
3920 if (trx_image == (Image *) NULL)
3921 ThrowDPCSimilarityException();
3923 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3924 magnitude_image=DestroyImage(magnitude_image);
3925 ry_image=DestroyImage(ry_image);
3926 if (try_image == (Image *) NULL)
3927 ThrowDPCSimilarityException();
3932 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
3934 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
3935 if (tx_image == (Image *) NULL)
3936 ThrowDPCSimilarityException();
3937 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
3938 target_image=DestroyImage(target_image);
3939 if (ty_image == (Image *) NULL)
3940 ThrowDPCSimilarityException();
3944 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
3945 if (magnitude_image == (Image *) NULL)
3946 ThrowDPCSimilarityException();
3950 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
3951 tx_image=DestroyImage(tx_image);
3952 if (trx_image == (Image *) NULL)
3953 ThrowDPCSimilarityException();
3955 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
3956 ty_image=DestroyImage(ty_image);
3957 magnitude_image=DestroyImage(magnitude_image);
3958 if (try_image == (Image *) NULL)
3959 ThrowDPCSimilarityException();
3964 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
3965 rx_image=DestroyImage(rx_image);
3966 tx_image=DestroyImage(tx_image);
3967 if (trx_image == (Image *) NULL)
3968 ThrowDPCSimilarityException();
3969 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
3970 ry_image=DestroyImage(ry_image);
3971 ty_image=DestroyImage(ty_image);
3972 if (try_image == (Image *) NULL)
3973 ThrowDPCSimilarityException();
3977 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
3978 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
3980 try_image=DestroyImage(try_image);
3981 if (status == MagickFalse)
3982 ThrowDPCSimilarityException();
3983 status=SIMMultiplyImage(trx_image,edge_factor,
3984 (
const ChannelStatistics *) NULL,exception);
3985 if (status == MagickFalse)
3986 ThrowDPCSimilarityException();
3990 SetGeometry(image,&geometry);
3991 geometry.width=image->columns;
3992 geometry.height=image->rows;
3993 (void) ResetImagePage(trx_image,
"0x0+0+0");
3994 dot_product_image=CropImage(trx_image,&geometry,exception);
3995 trx_image=DestroyImage(trx_image);
3996 if (dot_product_image == (Image *) NULL)
3997 ThrowDPCSimilarityException();
3998 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4002 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4004 if (status == MagickFalse)
4005 ThrowDPCSimilarityException();
4006 dot_product_image->depth=32;
4007 dot_product_image->colorspace=GRAYColorspace;
4008 dot_product_image->alpha_trait=UndefinedPixelTrait;
4009 status=SIMFilterImageNaNs(dot_product_image,exception);
4010 if (status == MagickFalse)
4011 ThrowDPCSimilarityException();
4012 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4013 if (status == MagickFalse)
4014 ThrowDPCSimilarityException();
4015 if ((QuantumScale*maxima) > 1.0)
4017 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4018 (
const ChannelStatistics *) NULL,exception);
4019 maxima=(double) QuantumRange;
4021 *similarity_metric=QuantumScale*maxima;
4022 return(dot_product_image);
4025static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4026 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4028#define ThrowMSESimilarityException() \
4030 if (alpha_image != (Image *) NULL) \
4031 alpha_image=DestroyImage(alpha_image); \
4032 if (beta_image != (Image *) NULL) \
4033 beta_image=DestroyImage(beta_image); \
4034 if (channel_statistics != (ChannelStatistics *) NULL) \
4035 channel_statistics=(ChannelStatistics *) \
4036 RelinquishMagickMemory(channel_statistics); \
4037 if (mean_image != (Image *) NULL) \
4038 mean_image=DestroyImage(mean_image); \
4039 if (mse_image != (Image *) NULL) \
4040 mse_image=DestroyImage(mse_image); \
4041 if (reconstruct_image != (Image *) NULL) \
4042 reconstruct_image=DestroyImage(reconstruct_image); \
4043 if (sum_image != (Image *) NULL) \
4044 sum_image=DestroyImage(sum_image); \
4045 if (alpha_image != (Image *) NULL) \
4046 alpha_image=DestroyImage(alpha_image); \
4047 return((Image *) NULL); \
4051 *channel_statistics = (ChannelStatistics *) NULL;
4057 *alpha_image = (Image *) NULL,
4058 *beta_image = (Image *) NULL,
4059 *mean_image = (Image *) NULL,
4060 *mse_image = (Image *) NULL,
4061 *reconstruct_image = (Image *) NULL,
4062 *sum_image = (Image *) NULL,
4063 *target_image = (Image *) NULL;
4066 status = MagickTrue;
4074 target_image=SIMSquareImage(image,exception);
4075 if (target_image == (Image *) NULL)
4076 ThrowMSESimilarityException();
4077 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4078 if (reconstruct_image == (Image *) NULL)
4079 ThrowMSESimilarityException();
4083 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4085 target_image=DestroyImage(target_image);
4086 if (alpha_image == (Image *) NULL)
4087 ThrowMSESimilarityException();
4088 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4089 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4090 if (status == MagickFalse)
4091 ThrowMSESimilarityException();
4095 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4096 MagickTrue,0,0,exception);
4097 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4098 reconstruct_image=DestroyImage(reconstruct_image);
4099 if (beta_image == (Image *) NULL)
4100 ThrowMSESimilarityException();
4101 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4102 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4103 if (status == MagickFalse)
4104 ThrowMSESimilarityException();
4108 sum_image=SIMSquareImage(reconstruct,exception);
4109 if (sum_image == (Image *) NULL)
4110 ThrowMSESimilarityException();
4111 channel_statistics=GetImageStatistics(sum_image,exception);
4112 if (channel_statistics == (ChannelStatistics *) NULL)
4113 ThrowMSESimilarityException();
4114 status=SetImageStorageClass(sum_image,DirectClass,exception);
4115 if (status == MagickFalse)
4116 ThrowMSESimilarityException();
4117 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4118 channel_statistics=(ChannelStatistics *)
4119 RelinquishMagickMemory(channel_statistics);
4120 if (status == MagickFalse)
4121 ThrowMSESimilarityException();
4125 AppendImageToList(&sum_image,alpha_image);
4126 AppendImageToList(&sum_image,beta_image);
4127 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4128 if (mean_image == (Image *) NULL)
4129 ThrowMSESimilarityException();
4130 sum_image=DestroyImage(sum_image);
4131 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4132 if (status == MagickFalse)
4133 ThrowMSESimilarityException();
4137 SetGeometry(image,&geometry);
4138 geometry.width=image->columns;
4139 geometry.height=image->rows;
4140 (void) ResetImagePage(mean_image,
"0x0+0+0");
4141 mse_image=CropImage(mean_image,&geometry,exception);
4142 mean_image=DestroyImage(mean_image);
4143 if (mse_image == (Image *) NULL)
4144 ThrowMSESimilarityException();
4148 (void) ResetImagePage(mse_image,
"0x0+0+0");
4149 (void) ClampImage(mse_image,exception);
4150 mse_image->depth=32;
4151 mse_image->colorspace=GRAYColorspace;
4152 mse_image->alpha_trait=UndefinedPixelTrait;
4153 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4154 if (status == MagickFalse)
4155 ThrowMSESimilarityException();
4156 status=NegateImage(mse_image,MagickFalse,exception);
4157 if (status == MagickFalse)
4158 ThrowMSESimilarityException();
4159 alpha_image=DestroyImage(alpha_image);
4160 beta_image=DestroyImage(beta_image);
4161 if ((QuantumScale*minima) < FLT_EPSILON)
4163 *similarity_metric=QuantumScale*minima;
4167static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4168 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4170#define ThrowNCCSimilarityException() \
4172 if (alpha_image != (Image *) NULL) \
4173 alpha_image=DestroyImage(alpha_image); \
4174 if (beta_image != (Image *) NULL) \
4175 beta_image=DestroyImage(beta_image); \
4176 if (channel_statistics != (ChannelStatistics *) NULL) \
4177 channel_statistics=(ChannelStatistics *) \
4178 RelinquishMagickMemory(channel_statistics); \
4179 if (correlation_image != (Image *) NULL) \
4180 correlation_image=DestroyImage(correlation_image); \
4181 if (divide_image != (Image *) NULL) \
4182 divide_image=DestroyImage(divide_image); \
4183 if (ncc_image != (Image *) NULL) \
4184 ncc_image=DestroyImage(ncc_image); \
4185 if (normalize_image != (Image *) NULL) \
4186 normalize_image=DestroyImage(normalize_image); \
4187 if (reconstruct_image != (Image *) NULL) \
4188 reconstruct_image=DestroyImage(reconstruct_image); \
4189 if (target_image != (Image *) NULL) \
4190 target_image=DestroyImage(target_image); \
4191 if (variance_image != (Image *) NULL) \
4192 variance_image=DestroyImage(variance_image); \
4193 return((Image *) NULL); \
4197 *channel_statistics = (ChannelStatistics *) NULL;
4203 *alpha_image = (Image *) NULL,
4204 *beta_image = (Image *) NULL,
4205 *correlation_image = (Image *) NULL,
4206 *divide_image = (Image *) NULL,
4207 *ncc_image = (Image *) NULL,
4208 *normalize_image = (Image *) NULL,
4209 *reconstruct_image = (Image *) NULL,
4210 *target_image = (Image *) NULL,
4211 *variance_image = (Image *) NULL;
4214 status = MagickTrue;
4222 target_image=SIMSquareImage(image,exception);
4223 if (target_image == (Image *) NULL)
4224 ThrowNCCSimilarityException();
4225 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4226 if (reconstruct_image == (Image *) NULL)
4227 ThrowNCCSimilarityException();
4231 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4233 target_image=DestroyImage(target_image);
4234 if (alpha_image == (Image *) NULL)
4235 ThrowNCCSimilarityException();
4236 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4237 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4239 if (status == MagickFalse)
4240 ThrowNCCSimilarityException();
4244 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4245 reconstruct_image=DestroyImage(reconstruct_image);
4246 if (beta_image == (Image *) NULL)
4247 ThrowNCCSimilarityException();
4248 target_image=SIMSquareImage(beta_image,exception);
4249 beta_image=DestroyImage(beta_image);
4250 if (target_image == (Image *) NULL)
4251 ThrowNCCSimilarityException();
4252 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4253 (
const ChannelStatistics *) NULL,exception);
4254 if (status == MagickFalse)
4255 ThrowNCCSimilarityException();
4259 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4260 target_image=DestroyImage(target_image);
4261 alpha_image=DestroyImage(alpha_image);
4262 if (variance_image == (Image *) NULL)
4263 ThrowNCCSimilarityException();
4267 channel_statistics=GetImageStatistics(reconstruct,exception);
4268 if (channel_statistics == (ChannelStatistics *) NULL)
4269 ThrowNCCSimilarityException();
4270 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4271 if (status == MagickFalse)
4272 ThrowNCCSimilarityException();
4273 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4275 channel_statistics=(ChannelStatistics *)
4276 RelinquishMagickMemory(channel_statistics);
4277 if (normalize_image == (Image *) NULL)
4278 ThrowNCCSimilarityException();
4279 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4280 normalize_image=DestroyImage(normalize_image);
4281 if (correlation_image == (Image *) NULL)
4282 ThrowNCCSimilarityException();
4286 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4287 correlation_image=DestroyImage(correlation_image);
4288 variance_image=DestroyImage(variance_image);
4289 if (divide_image == (Image *) NULL)
4290 ThrowNCCSimilarityException();
4294 SetGeometry(image,&geometry);
4295 geometry.width=image->columns;
4296 geometry.height=image->rows;
4297 (void) ResetImagePage(divide_image,
"0x0+0+0");
4298 ncc_image=CropImage(divide_image,&geometry,exception);
4299 divide_image=DestroyImage(divide_image);
4300 if (ncc_image == (Image *) NULL)
4301 ThrowNCCSimilarityException();
4305 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4306 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4307 if (status == MagickFalse)
4308 ThrowNCCSimilarityException();
4309 ncc_image->depth=32;
4310 ncc_image->colorspace=GRAYColorspace;
4311 ncc_image->alpha_trait=UndefinedPixelTrait;
4312 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4313 if (status == MagickFalse)
4314 ThrowNCCSimilarityException();
4315 if ((QuantumScale*maxima) > 1.0)
4317 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4318 (
const ChannelStatistics *) NULL,exception);
4319 maxima=(double) QuantumRange;
4321 *similarity_metric=QuantumScale*maxima;
4325static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4326 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4328#define ThrowPhaseSimilarityException() \
4330 if (correlation_image != (Image *) NULL) \
4331 correlation_image=DestroyImage(correlation_image); \
4332 if (fft_images != (Image *) NULL) \
4333 fft_images=DestroyImageList(fft_images); \
4334 if (gamma_image != (Image *) NULL) \
4335 gamma_image=DestroyImage(gamma_image); \
4336 if (magnitude_image != (Image *) NULL) \
4337 magnitude_image=DestroyImage(magnitude_image); \
4338 if (phase_image != (Image *) NULL) \
4339 phase_image=DestroyImage(phase_image); \
4340 if (reconstruct_image != (Image *) NULL) \
4341 reconstruct_image=DestroyImage(reconstruct_image); \
4342 if (reconstruct_magnitude != (Image *) NULL) \
4343 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4344 if (target_image != (Image *) NULL) \
4345 target_image=DestroyImage(target_image); \
4346 if (test_magnitude != (Image *) NULL) \
4347 test_magnitude=DestroyImage(test_magnitude); \
4348 return((Image *) NULL); \
4355 *correlation_image = (Image *) NULL,
4356 *fft_images = (Image *) NULL,
4357 *gamma_image = (Image *) NULL,
4358 *magnitude_image = (Image *) NULL,
4359 *phase_image = (Image *) NULL,
4360 *reconstruct_image = (Image *) NULL,
4361 *reconstruct_magnitude = (Image *) NULL,
4362 *target_image = (Image *) NULL,
4363 *test_magnitude = (Image *) NULL;
4366 status = MagickTrue;
4374 target_image=CloneImage(image,0,0,MagickTrue,exception);
4375 if (target_image == (Image *) NULL)
4376 ThrowPhaseSimilarityException();
4377 (void) ResetImagePage(target_image,
"0x0+0+0");
4378 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4379 &target_image->background_color);
4380 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4381 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4382 if (status == MagickFalse)
4383 ThrowPhaseSimilarityException();
4387 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4388 if (reconstruct_image == (Image *) NULL)
4389 ThrowPhaseSimilarityException();
4390 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4391 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4392 &reconstruct_image->background_color);
4393 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4394 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4395 if (status == MagickFalse)
4396 ThrowPhaseSimilarityException();
4400 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4401 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4402 if (fft_images == (Image *) NULL)
4403 ThrowPhaseSimilarityException();
4404 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4405 fft_images=DestroyImageList(fft_images);
4406 if (test_magnitude == (Image *) NULL)
4407 ThrowPhaseSimilarityException();
4408 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4409 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4411 if (fft_images == (Image *) NULL)
4412 ThrowPhaseSimilarityException();
4413 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4414 fft_images=DestroyImageList(fft_images);
4415 if (reconstruct_magnitude == (Image *) NULL)
4416 ThrowPhaseSimilarityException();
4417 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4418 if (magnitude_image == (Image *) NULL)
4419 ThrowPhaseSimilarityException();
4420 DisableCompositeClampUnlessSpecified(magnitude_image);
4421 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4422 MagickTrue,0,0,exception);
4426 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4427 magnitude_image,exception);
4428 target_image=DestroyImage(target_image);
4429 reconstruct_image=DestroyImage(reconstruct_image);
4430 test_magnitude=DestroyImage(test_magnitude);
4431 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4432 if (correlation_image == (Image *) NULL)
4433 ThrowPhaseSimilarityException();
4437 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4438 correlation_image=DestroyImage(correlation_image);
4439 if (gamma_image == (Image *) NULL)
4440 ThrowPhaseSimilarityException();
4444 SetGeometry(image,&geometry);
4445 geometry.width=image->columns;
4446 geometry.height=image->rows;
4447 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4448 phase_image=CropImage(gamma_image,&geometry,exception);
4449 gamma_image=DestroyImage(gamma_image);
4450 if (phase_image == (Image *) NULL)
4451 ThrowPhaseSimilarityException();
4452 (void) ResetImagePage(phase_image,
"0x0+0+0");
4456 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4457 if (status == MagickFalse)
4458 ThrowPhaseSimilarityException();
4459 phase_image->depth=32;
4460 phase_image->colorspace=GRAYColorspace;
4461 phase_image->alpha_trait=UndefinedPixelTrait;
4462 status=SIMFilterImageNaNs(phase_image,exception);
4463 if (status == MagickFalse)
4464 ThrowPhaseSimilarityException();
4465 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4466 if (status == MagickFalse)
4467 ThrowPhaseSimilarityException();
4468 magnitude_image=DestroyImage(magnitude_image);
4469 if ((QuantumScale*maxima) > 1.0)
4471 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4472 (
const ChannelStatistics *) NULL,exception);
4473 maxima=(double) QuantumRange;
4475 *similarity_metric=QuantumScale*maxima;
4476 return(phase_image);
4479static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4480 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4483 *psnr_image = (Image *) NULL;
4485 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4487 if (psnr_image == (Image *) NULL)
4489 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4490 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4494static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4495 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4498 *rmse_image = (Image *) NULL;
4500 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4502 if (rmse_image == (Image *) NULL)
4504 *similarity_metric=sqrt(*similarity_metric);
4509static double GetSimilarityMetric(
const Image *image,
4510 const Image *reconstruct_image,
const MetricType metric,
4511 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4514 *channel_similarity,
4518 *sans_exception = AcquireExceptionInfo();
4524 status = MagickTrue;
4530 length = MaxPixelChannels+1UL;
4532 SetGeometry(reconstruct_image,&geometry);
4533 geometry.x=x_offset;
4534 geometry.y=y_offset;
4535 similarity_image=CropImage(image,&geometry,sans_exception);
4536 sans_exception=DestroyExceptionInfo(sans_exception);
4537 if (similarity_image == (Image *) NULL)
4542 channel_similarity=(
double *) AcquireQuantumMemory(length,
4543 sizeof(*channel_similarity));
4544 if (channel_similarity == (
double *) NULL)
4545 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4546 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4549 case AbsoluteErrorMetric:
4551 status=GetAESimilarity(similarity_image,reconstruct_image,
4552 channel_similarity,exception);
4555 case DotProductCorrelationErrorMetric:
4557 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4558 channel_similarity,exception);
4561 case FuzzErrorMetric:
4563 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4564 channel_similarity,exception);
4567 case MeanAbsoluteErrorMetric:
4569 status=GetMAESimilarity(similarity_image,reconstruct_image,
4570 channel_similarity,exception);
4573 case MeanErrorPerPixelErrorMetric:
4575 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4576 channel_similarity,exception);
4579 case MeanSquaredErrorMetric:
4581 status=GetMSESimilarity(similarity_image,reconstruct_image,
4582 channel_similarity,exception);
4585 case NormalizedCrossCorrelationErrorMetric:
4587 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4588 channel_similarity,exception);
4591 case PeakAbsoluteErrorMetric:
4593 status=GetPASimilarity(similarity_image,reconstruct_image,
4594 channel_similarity,exception);
4597 case PeakSignalToNoiseRatioErrorMetric:
4599 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4600 channel_similarity,exception);
4603 case PerceptualHashErrorMetric:
4605 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4606 channel_similarity,exception);
4609 case PhaseCorrelationErrorMetric:
4611 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4612 channel_similarity,exception);
4615 case PixelDifferenceCountErrorMetric:
4617 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4618 channel_similarity,exception);
4621 case RootMeanSquaredErrorMetric:
4622 case UndefinedErrorMetric:
4625 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4626 channel_similarity,exception);
4629 case StructuralDissimilarityErrorMetric:
4631 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4632 channel_similarity,exception);
4635 case StructuralSimilarityErrorMetric:
4637 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4638 channel_similarity,exception);
4642 similarity_image=DestroyImage(similarity_image);
4643 similarity=channel_similarity[CompositePixelChannel];
4644 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4645 if (status == MagickFalse)
4650MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4651 const MetricType metric,
const double similarity_threshold,
4652 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4654#define SimilarityImageTag "Similarity/Image"
4670 *similarity_image = (Image *) NULL;
4673 status = MagickTrue;
4679 similarity_info = { 0.0, 0, 0 };
4684 assert(image != (
const Image *) NULL);
4685 assert(image->signature == MagickCoreSignature);
4686 assert(exception != (ExceptionInfo *) NULL);
4687 assert(exception->signature == MagickCoreSignature);
4688 assert(offset != (RectangleInfo *) NULL);
4689 if (IsEventLogging() != MagickFalse)
4690 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4691 SetGeometry(reconstruct,offset);
4692 *similarity_metric=0.0;
4695#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4697 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4698 if (artifact == (
const char *) NULL)
4699 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4700 if (((artifact == (
const char *) NULL) ||
4701 (IsStringTrue(artifact) != MagickFalse)) &&
4702 ((image->channels & ReadMaskChannel) == 0))
4705 case DotProductCorrelationErrorMetric:
4707 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4708 similarity_metric,exception);
4709 return(similarity_image);
4711 case MeanSquaredErrorMetric:
4713 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4714 similarity_metric,exception);
4715 return(similarity_image);
4717 case NormalizedCrossCorrelationErrorMetric:
4719 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4720 similarity_metric,exception);
4721 return(similarity_image);
4723 case PeakSignalToNoiseRatioErrorMetric:
4725 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4726 similarity_metric,exception);
4727 return(similarity_image);
4729 case PhaseCorrelationErrorMetric:
4731 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4732 similarity_metric,exception);
4733 return(similarity_image);
4735 case RootMeanSquaredErrorMetric:
4736 case UndefinedErrorMetric:
4738 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4739 similarity_metric,exception);
4740 return(similarity_image);
4747 if ((image->columns < reconstruct->columns) ||
4748 (image->rows < reconstruct->rows))
4750 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4751 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4752 return((Image *) NULL);
4754 similarity_image=CloneImage(image,image->columns-reconstruct->columns+1,
4755 image->rows-reconstruct->rows+1,MagickTrue,exception);
4756 if (similarity_image == (Image *) NULL)
4757 return((Image *) NULL);
4758 similarity_image->depth=32;
4759 similarity_image->colorspace=GRAYColorspace;
4760 similarity_image->alpha_trait=UndefinedPixelTrait;
4761 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4762 if (status == MagickFalse)
4763 return(DestroyImage(similarity_image));
4767 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4768 similarity_info.x,similarity_info.y,exception);
4769 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4770#if defined(MAGICKCORE_OPENMP_SUPPORT)
4771 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4772 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4774 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4780 threshold_trigger = MagickFalse;
4786 channel_info = similarity_info;
4791 if (status == MagickFalse)
4793 if (threshold_trigger != MagickFalse)
4795 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4796 similarity_image->columns,1,exception);
4797 if (q == (Quantum *) NULL)
4802 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4807 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4811 case DotProductCorrelationErrorMetric:
4812 case NormalizedCrossCorrelationErrorMetric:
4813 case PeakSignalToNoiseRatioErrorMetric:
4814 case PhaseCorrelationErrorMetric:
4815 case StructuralSimilarityErrorMetric:
4817 if (similarity <= channel_info.similarity)
4819 channel_info.similarity=similarity;
4826 if (similarity >= channel_info.similarity)
4828 channel_info.similarity=similarity;
4834 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4836 PixelChannel channel = GetPixelChannelChannel(image,i);
4837 PixelTrait traits = GetPixelChannelTraits(image,channel);
4838 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4840 if (((traits & UpdatePixelTrait) == 0) ||
4841 ((similarity_traits & UpdatePixelTrait) == 0))
4845 case DotProductCorrelationErrorMetric:
4846 case NormalizedCrossCorrelationErrorMetric:
4847 case PeakSignalToNoiseRatioErrorMetric:
4848 case PhaseCorrelationErrorMetric:
4849 case StructuralSimilarityErrorMetric:
4851 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4852 QuantumRange*similarity),q);
4857 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4858 QuantumRange*(1.0-similarity)),q);
4863 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4865#if defined(MAGICKCORE_OPENMP_SUPPORT)
4866 #pragma omp critical (MagickCore_GetSimilarityMetric)
4870 case DotProductCorrelationErrorMetric:
4871 case NormalizedCrossCorrelationErrorMetric:
4872 case PeakSignalToNoiseRatioErrorMetric:
4873 case PhaseCorrelationErrorMetric:
4874 case StructuralSimilarityErrorMetric:
4876 if (similarity_threshold != DefaultSimilarityThreshold)
4877 if (channel_info.similarity >= similarity_threshold)
4878 threshold_trigger=MagickTrue;
4879 if (channel_info.similarity >= similarity_info.similarity)
4880 similarity_info=channel_info;
4885 if (similarity_threshold != DefaultSimilarityThreshold)
4886 if (channel_info.similarity < similarity_threshold)
4887 threshold_trigger=MagickTrue;
4888 if (channel_info.similarity < similarity_info.similarity)
4889 similarity_info=channel_info;
4893 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4895 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4901 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4902 if (proceed == MagickFalse)
4906 similarity_view=DestroyCacheView(similarity_view);
4907 if (status == MagickFalse)
4908 similarity_image=DestroyImage(similarity_image);
4909 *similarity_metric=similarity_info.similarity;
4910 if (fabs(*similarity_metric) < MagickEpsilon)
4911 *similarity_metric=0.0;
4912 offset->x=similarity_info.x;
4913 offset->y=similarity_info.y;
4914 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4915 GetMagickPrecision(),*similarity_metric);
4916 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4917 GetMagickPrecision(),(
double) offset->x);
4918 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4919 GetMagickPrecision(),(
double) offset->y);
4920 return(similarity_image);