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)
332 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
333 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
334 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
335 image_view=AcquireVirtualCacheView(image,exception);
336 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
337#if defined(MAGICKCORE_OPENMP_SUPPORT)
338 #pragma omp parallel for schedule(static) shared(similarity,status) \
339 magick_number_threads(image,image,rows,1)
341 for (y=0; y < (ssize_t) rows; y++)
348 channel_similarity[MaxPixelChannels+1] = { 0.0 };
353 if (status == MagickFalse)
355 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
356 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
357 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
362 for (x=0; x < (ssize_t) columns; x++)
371 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
372 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
374 p+=(ptrdiff_t) GetPixelChannels(image);
375 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
378 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
379 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
380 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
385 PixelChannel channel = GetPixelChannelChannel(image,i);
386 PixelTrait traits = GetPixelChannelTraits(image,channel);
387 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
389 if (((traits & UpdatePixelTrait) == 0) ||
390 ((reconstruct_traits & UpdatePixelTrait) == 0))
392 if (channel == AlphaPixelChannel)
393 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
396 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
397 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
399 double ae = fabs(QuantumScale*error);
400 channel_similarity[i]+=ae;
401 channel_similarity[CompositePixelChannel]+=ae;
404 p+=(ptrdiff_t) GetPixelChannels(image);
405 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
407#if defined(MAGICKCORE_OPENMP_SUPPORT)
408 #pragma omp critical (MagickCore_GetAESimilarity)
414 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
416 PixelChannel channel = GetPixelChannelChannel(image,j);
417 PixelTrait traits = GetPixelChannelTraits(image,channel);
418 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
420 if (((traits & UpdatePixelTrait) == 0) ||
421 ((reconstruct_traits & UpdatePixelTrait) == 0))
423 similarity[j]+=channel_similarity[j];
425 similarity[CompositePixelChannel]+=
426 channel_similarity[CompositePixelChannel];
429 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
430 reconstruct_view=DestroyCacheView(reconstruct_view);
431 image_view=DestroyCacheView(image_view);
432 area=MagickSafeReciprocal((
double) columns*rows);
433 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
435 similarity[CompositePixelChannel]*=area;
439static MagickBooleanType GetDPCSimilarity(
const Image *image,
440 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
442#define SimilarityImageTag "Similarity/Image"
450 *reconstruct_statistics;
453 norm[MaxPixelChannels+1] = { 0.0 },
454 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
473 image_statistics=GetImageStatistics(image,exception);
474 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
475 if ((image_statistics == (ChannelStatistics *) NULL) ||
476 (reconstruct_statistics == (ChannelStatistics *) NULL))
478 if (image_statistics != (ChannelStatistics *) NULL)
479 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
481 if (reconstruct_statistics != (ChannelStatistics *) NULL)
482 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
483 reconstruct_statistics);
486 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
487 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
488 image_view=AcquireVirtualCacheView(image,exception);
489 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
490#if defined(MAGICKCORE_OPENMP_SUPPORT)
491 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
492 magick_number_threads(image,image,rows,1)
494 for (y=0; y < (ssize_t) rows; y++)
501 channel_norm[MaxPixelChannels+1] = { 0.0 },
502 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
503 channel_similarity[MaxPixelChannels+1] = { 0.0 };
508 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
509 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
510 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
515 for (x=0; x < (ssize_t) columns; x++)
524 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
525 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
527 p+=(ptrdiff_t) GetPixelChannels(image);
528 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
531 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
532 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
533 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
539 PixelChannel channel = GetPixelChannelChannel(image,i);
540 PixelTrait traits = GetPixelChannelTraits(image,channel);
541 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
543 if (((traits & UpdatePixelTrait) == 0) ||
544 ((reconstruct_traits & UpdatePixelTrait) == 0))
546 if (channel == AlphaPixelChannel)
548 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
549 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
550 channel,q)-reconstruct_statistics[channel].mean);
554 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
555 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
556 q)-reconstruct_statistics[channel].mean);
558 channel_similarity[i]+=alpha*beta;
559 channel_norm[i]+=alpha*alpha;
560 channel_reconstruct_norm[i]+=beta*beta;
562 p+=(ptrdiff_t) GetPixelChannels(image);
563 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
565#if defined(MAGICKCORE_OPENMP_SUPPORT)
566 #pragma omp critical (MagickCore_GetDPCSimilarity)
572 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
574 PixelChannel channel = GetPixelChannelChannel(image,j);
575 PixelTrait traits = GetPixelChannelTraits(image,channel);
576 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
578 if (((traits & UpdatePixelTrait) == 0) ||
579 ((reconstruct_traits & UpdatePixelTrait) == 0))
581 similarity[j]+=channel_similarity[j];
582 similarity[CompositePixelChannel]+=channel_similarity[j];
583 norm[j]+=channel_norm[j];
584 norm[CompositePixelChannel]+=channel_norm[j];
585 reconstruct_norm[j]+=channel_reconstruct_norm[j];
586 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
589 if (image->progress_monitor != (MagickProgressMonitor) NULL)
594#if defined(MAGICKCORE_OPENMP_SUPPORT)
598 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
599 if (proceed == MagickFalse)
606 reconstruct_view=DestroyCacheView(reconstruct_view);
607 image_view=DestroyCacheView(image_view);
611 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
613 PixelChannel channel = GetPixelChannelChannel(image,k);
614 PixelTrait traits = GetPixelChannelTraits(image,channel);
615 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
617 if (((traits & UpdatePixelTrait) == 0) ||
618 ((reconstruct_traits & UpdatePixelTrait) == 0))
620 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
622 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
623 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
627 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
628 reconstruct_statistics);
629 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
634static MagickBooleanType GetFUZZSimilarity(
const Image *image,
635 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
659 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
660 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
661 image_view=AcquireVirtualCacheView(image,exception);
662 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
663#if defined(MAGICKCORE_OPENMP_SUPPORT)
664 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
665 magick_number_threads(image,image,rows,1)
667 for (y=0; y < (ssize_t) rows; y++)
675 channel_similarity[MaxPixelChannels+1] = { 0.0 };
680 if (status == MagickFalse)
682 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
683 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
684 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
689 for (x=0; x < (ssize_t) columns; x++)
698 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
699 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
701 p+=(ptrdiff_t) GetPixelChannels(image);
702 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
705 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
706 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
707 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
712 PixelChannel channel = GetPixelChannelChannel(image,i);
713 PixelTrait traits = GetPixelChannelTraits(image,channel);
714 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
716 if (((traits & UpdatePixelTrait) == 0) ||
717 ((reconstruct_traits & UpdatePixelTrait) == 0))
719 if (channel == AlphaPixelChannel)
720 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
723 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
724 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
726 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
727 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
732 p+=(ptrdiff_t) GetPixelChannels(image);
733 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
735#if defined(MAGICKCORE_OPENMP_SUPPORT)
736 #pragma omp critical (MagickCore_GetFUZZSimilarity)
743 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
745 PixelChannel channel = GetPixelChannelChannel(image,j);
746 PixelTrait traits = GetPixelChannelTraits(image,channel);
747 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
749 if (((traits & UpdatePixelTrait) == 0) ||
750 ((reconstruct_traits & UpdatePixelTrait) == 0))
752 similarity[j]+=channel_similarity[j];
754 similarity[CompositePixelChannel]+=
755 channel_similarity[CompositePixelChannel];
758 reconstruct_view=DestroyCacheView(reconstruct_view);
759 image_view=DestroyCacheView(image_view);
760 area=MagickSafeReciprocal(area);
761 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
763 PixelChannel channel = GetPixelChannelChannel(image,k);
764 PixelTrait traits = GetPixelChannelTraits(image,channel);
765 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
767 if (((traits & UpdatePixelTrait) == 0) ||
768 ((reconstruct_traits & UpdatePixelTrait) == 0))
772 similarity[CompositePixelChannel]*=area;
776static MagickBooleanType GetMAESimilarity(
const Image *image,
777 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
800 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
801 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
802 image_view=AcquireVirtualCacheView(image,exception);
803 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
804#if defined(MAGICKCORE_OPENMP_SUPPORT)
805 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
806 magick_number_threads(image,image,rows,1)
808 for (y=0; y < (ssize_t) rows; y++)
816 channel_similarity[MaxPixelChannels+1] = { 0.0 };
821 if (status == MagickFalse)
823 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
824 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
825 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
830 for (x=0; x < (ssize_t) columns; x++)
839 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
840 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
842 p+=(ptrdiff_t) GetPixelChannels(image);
843 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
846 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
847 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
848 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
853 PixelChannel channel = GetPixelChannelChannel(image,i);
854 PixelTrait traits = GetPixelChannelTraits(image,channel);
855 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
857 if (((traits & UpdatePixelTrait) == 0) ||
858 ((reconstruct_traits & UpdatePixelTrait) == 0))
860 if (channel == AlphaPixelChannel)
861 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
862 reconstruct_image,channel,q));
864 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
866 channel_similarity[i]+=error;
867 channel_similarity[CompositePixelChannel]+=error;
870 p+=(ptrdiff_t) GetPixelChannels(image);
871 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
873#if defined(MAGICKCORE_OPENMP_SUPPORT)
874 #pragma omp critical (MagickCore_GetMAESimilarity)
881 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
883 PixelChannel channel = GetPixelChannelChannel(image,j);
884 PixelTrait traits = GetPixelChannelTraits(image,channel);
885 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
887 if (((traits & UpdatePixelTrait) == 0) ||
888 ((reconstruct_traits & UpdatePixelTrait) == 0))
890 similarity[j]+=channel_similarity[j];
892 similarity[CompositePixelChannel]+=
893 channel_similarity[CompositePixelChannel];
896 reconstruct_view=DestroyCacheView(reconstruct_view);
897 image_view=DestroyCacheView(image_view);
898 area=MagickSafeReciprocal(area);
899 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
901 PixelChannel channel = GetPixelChannelChannel(image,k);
902 PixelTrait traits = GetPixelChannelTraits(image,channel);
903 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
905 if (((traits & UpdatePixelTrait) == 0) ||
906 ((reconstruct_traits & UpdatePixelTrait) == 0))
910 similarity[CompositePixelChannel]*=area;
911 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
915static MagickBooleanType GetMEPPSimilarity(Image *image,
916 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
924 maximum_error = -MagickMaximumValue,
941 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
942 image_view=AcquireVirtualCacheView(image,exception);
943 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
944#if defined(MAGICKCORE_OPENMP_SUPPORT)
945 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
946 magick_number_threads(image,image,rows,1)
948 for (y=0; y < (ssize_t) rows; y++)
956 channel_similarity[MaxPixelChannels+1] = { 0.0 },
957 channel_maximum_error = maximum_error,
958 channel_mean_error = 0.0;
963 if (status == MagickFalse)
965 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
966 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
967 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
972 for (x=0; x < (ssize_t) columns; x++)
981 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
982 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
984 p+=(ptrdiff_t) GetPixelChannels(image);
985 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
988 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
989 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
990 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
995 PixelChannel channel = GetPixelChannelChannel(image,i);
996 PixelTrait traits = GetPixelChannelTraits(image,channel);
997 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
999 if (((traits & UpdatePixelTrait) == 0) ||
1000 ((reconstruct_traits & UpdatePixelTrait) == 0))
1002 if (channel == AlphaPixelChannel)
1003 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
1004 reconstruct_image,channel,q));
1006 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1008 channel_similarity[i]+=error;
1009 channel_similarity[CompositePixelChannel]+=error;
1010 channel_mean_error+=error*error;
1011 if (error > channel_maximum_error)
1012 channel_maximum_error=error;
1015 p+=(ptrdiff_t) GetPixelChannels(image);
1016 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1018#if defined(MAGICKCORE_OPENMP_SUPPORT)
1019 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1026 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1028 PixelChannel channel = GetPixelChannelChannel(image,j);
1029 PixelTrait traits = GetPixelChannelTraits(image,channel);
1030 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1032 if (((traits & UpdatePixelTrait) == 0) ||
1033 ((reconstruct_traits & UpdatePixelTrait) == 0))
1035 similarity[j]+=channel_similarity[j];
1037 similarity[CompositePixelChannel]+=
1038 channel_similarity[CompositePixelChannel];
1039 mean_error+=channel_mean_error;
1040 if (channel_maximum_error > maximum_error)
1041 maximum_error=channel_maximum_error;
1044 reconstruct_view=DestroyCacheView(reconstruct_view);
1045 image_view=DestroyCacheView(image_view);
1046 area=MagickSafeReciprocal(area);
1047 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1049 PixelChannel channel = GetPixelChannelChannel(image,k);
1050 PixelTrait traits = GetPixelChannelTraits(image,channel);
1051 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1053 if (((traits & UpdatePixelTrait) == 0) ||
1054 ((reconstruct_traits & UpdatePixelTrait) == 0))
1056 similarity[k]*=area;
1058 similarity[CompositePixelChannel]*=area;
1059 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1060 image->error.mean_error_per_pixel=QuantumRange*
1061 similarity[CompositePixelChannel];
1062 image->error.normalized_mean_error=mean_error*area;
1063 image->error.normalized_maximum_error=maximum_error;
1067static MagickBooleanType GetMSESimilarity(
const Image *image,
1068 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1078 status = MagickTrue;
1091 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1092 image_view=AcquireVirtualCacheView(image,exception);
1093 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1094#if defined(MAGICKCORE_OPENMP_SUPPORT)
1095 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1096 magick_number_threads(image,image,rows,1)
1098 for (y=0; y < (ssize_t) rows; y++)
1106 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1111 if (status == MagickFalse)
1113 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1114 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1115 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1120 for (x=0; x < (ssize_t) columns; x++)
1129 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1130 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1132 p+=(ptrdiff_t) GetPixelChannels(image);
1133 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1136 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1137 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1138 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1143 PixelChannel channel = GetPixelChannelChannel(image,i);
1144 PixelTrait traits = GetPixelChannelTraits(image,channel);
1145 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1147 if (((traits & UpdatePixelTrait) == 0) ||
1148 ((reconstruct_traits & UpdatePixelTrait) == 0))
1150 if (channel == AlphaPixelChannel)
1151 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1152 reconstruct_image,channel,q));
1154 error=QuantumScale*(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1156 channel_similarity[i]+=error*error;
1157 channel_similarity[CompositePixelChannel]+=error*error;
1160 p+=(ptrdiff_t) GetPixelChannels(image);
1161 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1163#if defined(MAGICKCORE_OPENMP_SUPPORT)
1164 #pragma omp critical (MagickCore_GetMSESimilarity)
1171 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1173 PixelChannel channel = GetPixelChannelChannel(image,j);
1174 PixelTrait traits = GetPixelChannelTraits(image,channel);
1175 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1177 if (((traits & UpdatePixelTrait) == 0) ||
1178 ((reconstruct_traits & UpdatePixelTrait) == 0))
1180 similarity[j]+=channel_similarity[j];
1182 similarity[CompositePixelChannel]+=
1183 channel_similarity[CompositePixelChannel];
1186 reconstruct_view=DestroyCacheView(reconstruct_view);
1187 image_view=DestroyCacheView(image_view);
1188 area=MagickSafeReciprocal(area);
1189 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1191 PixelChannel channel = GetPixelChannelChannel(image,k);
1192 PixelTrait traits = GetPixelChannelTraits(image,channel);
1193 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1195 if (((traits & UpdatePixelTrait) == 0) ||
1196 ((reconstruct_traits & UpdatePixelTrait) == 0))
1198 similarity[k]*=area;
1200 similarity[CompositePixelChannel]*=area;
1201 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1205static MagickBooleanType GetNCCSimilarity(
const Image *image,
1206 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1214 *reconstruct_statistics;
1217 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1218 variance[MaxPixelChannels+1] = { 0.0 };
1221 status = MagickTrue;
1237 image_statistics=GetImageStatistics(image,exception);
1238 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1239 if ((image_statistics == (ChannelStatistics *) NULL) ||
1240 (reconstruct_statistics == (ChannelStatistics *) NULL))
1242 if (image_statistics != (ChannelStatistics *) NULL)
1243 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1245 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1246 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1247 reconstruct_statistics);
1248 return(MagickFalse);
1250 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1251 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1252 image_view=AcquireVirtualCacheView(image,exception);
1253 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1254#if defined(MAGICKCORE_OPENMP_SUPPORT)
1255 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1256 magick_number_threads(image,image,rows,1)
1258 for (y=0; y < (ssize_t) rows; y++)
1265 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1266 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1267 channel_variance[MaxPixelChannels+1] = { 0.0 };
1272 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1273 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1274 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1279 for (x=0; x < (ssize_t) columns; x++)
1288 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1289 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1291 p+=(ptrdiff_t) GetPixelChannels(image);
1292 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1295 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1296 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1297 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1303 PixelChannel channel = GetPixelChannelChannel(image,i);
1304 PixelTrait traits = GetPixelChannelTraits(image,channel);
1305 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1307 if (((traits & UpdatePixelTrait) == 0) ||
1308 ((reconstruct_traits & UpdatePixelTrait) == 0))
1310 if (channel == AlphaPixelChannel)
1312 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1313 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1314 channel,q)-reconstruct_statistics[channel].mean);
1318 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
1319 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
1320 q)-reconstruct_statistics[channel].mean);
1322 channel_similarity[i]+=alpha*beta;
1323 channel_variance[i]+=alpha*alpha;
1324 channel_reconstruct_variance[i]+=beta*beta;
1326 p+=(ptrdiff_t) GetPixelChannels(image);
1327 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1329#if defined(MAGICKCORE_OPENMP_SUPPORT)
1330 #pragma omp critical (MagickCore_GetNCCSimilarity)
1336 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1338 PixelChannel channel = GetPixelChannelChannel(image,j);
1339 PixelTrait traits = GetPixelChannelTraits(image,channel);
1340 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1342 if (((traits & UpdatePixelTrait) == 0) ||
1343 ((reconstruct_traits & UpdatePixelTrait) == 0))
1345 similarity[j]+=channel_similarity[j];
1346 similarity[CompositePixelChannel]+=channel_similarity[j];
1347 variance[j]+=channel_variance[j];
1348 variance[CompositePixelChannel]+=channel_variance[j];
1349 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1350 reconstruct_variance[CompositePixelChannel]+=
1351 channel_reconstruct_variance[j];
1354 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1359#if defined(MAGICKCORE_OPENMP_SUPPORT)
1363 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1364 if (proceed == MagickFalse)
1371 reconstruct_view=DestroyCacheView(reconstruct_view);
1372 image_view=DestroyCacheView(image_view);
1376 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1378 PixelChannel channel = GetPixelChannelChannel(image,k);
1379 PixelTrait traits = GetPixelChannelTraits(image,channel);
1380 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1382 if (((traits & UpdatePixelTrait) == 0) ||
1383 ((reconstruct_traits & UpdatePixelTrait) == 0))
1385 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1386 sqrt(reconstruct_variance[k]));
1388 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1389 variance[CompositePixelChannel])*sqrt(
1390 reconstruct_variance[CompositePixelChannel]));
1394 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1395 reconstruct_statistics);
1396 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1401static MagickBooleanType GetPASimilarity(
const Image *image,
1402 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1409 status = MagickTrue;
1421 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1422 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1423 image_view=AcquireVirtualCacheView(image,exception);
1424 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1425#if defined(MAGICKCORE_OPENMP_SUPPORT)
1426 #pragma omp parallel for schedule(static) shared(similarity,status) \
1427 magick_number_threads(image,image,rows,1)
1429 for (y=0; y < (ssize_t) rows; y++)
1436 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1441 if (status == MagickFalse)
1443 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1444 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1445 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1450 for (x=0; x < (ssize_t) columns; x++)
1459 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1460 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1462 p+=(ptrdiff_t) GetPixelChannels(image);
1463 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1466 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1467 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1473 PixelChannel channel = GetPixelChannelChannel(image,i);
1474 PixelTrait traits = GetPixelChannelTraits(image,channel);
1475 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1477 if (((traits & UpdatePixelTrait) == 0) ||
1478 ((reconstruct_traits & UpdatePixelTrait) == 0))
1480 if (channel == AlphaPixelChannel)
1481 distance=QuantumScale*fabs((
double) p[i]-(
double)
1482 GetPixelChannel(reconstruct_image,channel,q));
1484 distance=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(
1485 reconstruct_image,channel,q));
1486 if (distance > channel_similarity[i])
1487 channel_similarity[i]=distance;
1488 if (distance > channel_similarity[CompositePixelChannel])
1489 channel_similarity[CompositePixelChannel]=distance;
1491 p+=(ptrdiff_t) GetPixelChannels(image);
1492 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1494#if defined(MAGICKCORE_OPENMP_SUPPORT)
1495 #pragma omp critical (MagickCore_GetPASimilarity)
1501 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1503 PixelChannel channel = GetPixelChannelChannel(image,j);
1504 PixelTrait traits = GetPixelChannelTraits(image,channel);
1505 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1507 if (((traits & UpdatePixelTrait) == 0) ||
1508 ((reconstruct_traits & UpdatePixelTrait) == 0))
1510 if (channel_similarity[j] > similarity[j])
1511 similarity[j]=channel_similarity[j];
1513 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1514 similarity[CompositePixelChannel]=
1515 channel_similarity[CompositePixelChannel];
1518 reconstruct_view=DestroyCacheView(reconstruct_view);
1519 image_view=DestroyCacheView(image_view);
1523static MagickBooleanType GetPDCSimilarity(
const Image *image,
1524 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1535 status = MagickTrue;
1548 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1549 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1550 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1551 image_view=AcquireVirtualCacheView(image,exception);
1552 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1553#if defined(MAGICKCORE_OPENMP_SUPPORT)
1554 #pragma omp parallel for schedule(static) shared(similarity,status) \
1555 magick_number_threads(image,image,rows,1)
1557 for (y=0; y < (ssize_t) rows; y++)
1564 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1569 if (status == MagickFalse)
1571 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1572 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1573 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1578 for (x=0; x < (ssize_t) columns; x++)
1590 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1591 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1593 p+=(ptrdiff_t) GetPixelChannels(image);
1594 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1597 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1598 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1599 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1604 PixelChannel channel = GetPixelChannelChannel(image,i);
1605 PixelTrait traits = GetPixelChannelTraits(image,channel);
1606 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1608 if (((traits & UpdatePixelTrait) == 0) ||
1609 ((reconstruct_traits & UpdatePixelTrait) == 0))
1611 if (channel == AlphaPixelChannel)
1612 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1615 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1616 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1618 channel_similarity[i]++;
1623 channel_similarity[CompositePixelChannel]++;
1624 p+=(ptrdiff_t) GetPixelChannels(image);
1625 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1627#if defined(MAGICKCORE_OPENMP_SUPPORT)
1628 #pragma omp critical (MagickCore_GetPDCSimilarity)
1634 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1636 PixelChannel channel = GetPixelChannelChannel(image,j);
1637 PixelTrait traits = GetPixelChannelTraits(image,channel);
1638 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1640 if (((traits & UpdatePixelTrait) == 0) ||
1641 ((reconstruct_traits & UpdatePixelTrait) == 0))
1643 similarity[j]+=channel_similarity[j];
1645 similarity[CompositePixelChannel]+=
1646 channel_similarity[CompositePixelChannel];
1649 reconstruct_view=DestroyCacheView(reconstruct_view);
1650 image_view=DestroyCacheView(image_view);
1651 area=MagickSafeReciprocal((
double) columns*rows);
1652 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1653 similarity[k]*=area;
1654 similarity[CompositePixelChannel]*=area;
1658static MagickBooleanType DFTPhaseSpectrum(
const Image *image,
const ssize_t u,
1659 const ssize_t v,
double *phase,ExceptionInfo *exception)
1661#define PhaseImageTag "Phase/Image"
1667 channel_imag[MaxPixelChannels+1] = { 0.0 },
1668 channel_real[MaxPixelChannels+1] = { 0.0 };
1681 image_view=AcquireVirtualCacheView(image,exception);
1682 for (y=0; y < (ssize_t) image->rows; y++)
1690 if (status == MagickFalse)
1692 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1693 if (p == (
const Quantum *) NULL)
1698 for (x=0; x < (ssize_t) image->columns; x++)
1707 angle=MagickPI*((u*x/(double) image->rows)+(v*y/(double) image->columns));
1708 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1709 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1711 PixelChannel channel = GetPixelChannelChannel(image,i);
1712 PixelTrait traits = GetPixelChannelTraits(image,channel);
1713 if (traits == UndefinedPixelTrait)
1715 if (channel == AlphaPixelChannel)
1717 channel_real[i]+=(QuantumScale*p[i])*cos(angle);
1718 channel_imag[i]-=(QuantumScale*p[i])*sin(angle);
1722 channel_real[i]+=(QuantumScale*Sa*p[i])*cos(angle);
1723 channel_imag[i]-=(QuantumScale*Sa*p[i])*sin(angle);
1726 p+=(ptrdiff_t) GetPixelChannels(image);
1729 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1730 phase[k]=atan2(channel_imag[k],channel_real[k]);
1731 phase[CompositePixelChannel]=atan2(channel_imag[CompositePixelChannel],
1732 channel_real[CompositePixelChannel]);
1733 image_view=DestroyCacheView(image_view);
1737static MagickBooleanType GetPHASESimilarity(
const Image *image,
1738 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1748 status = MagickTrue;
1761 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1762 image_view=AcquireVirtualCacheView(image,exception);
1763 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1764#if defined(MAGICKCORE_OPENMP_SUPPORT)
1765 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1766 magick_number_threads(image,image,rows,1)
1768 for (y=0; y < (ssize_t) rows; y++)
1776 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1781 if (status == MagickFalse)
1783 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1784 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1785 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1790 for (x=0; x < (ssize_t) columns; x++)
1793 phase[MaxPixelChannels+1] = { 0.0 },
1794 reconstruct_phase[MaxPixelChannels+1] = { 0.0 };
1799 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1800 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1802 p+=(ptrdiff_t) GetPixelChannels(image);
1803 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1806 status=DFTPhaseSpectrum(image,x,y,phase,exception);
1807 if (status == MagickFalse)
1809 status=DFTPhaseSpectrum(reconstruct_image,x,y,reconstruct_phase,
1811 if (status == MagickFalse)
1813 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1818 PixelChannel channel = GetPixelChannelChannel(image,i);
1819 PixelTrait traits = GetPixelChannelTraits(image,channel);
1820 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1822 if (((traits & UpdatePixelTrait) == 0) ||
1823 ((reconstruct_traits & UpdatePixelTrait) == 0))
1825 delta=phase[i]-reconstruct_phase[i];
1826 channel_similarity[i]+=cos(delta);
1827 channel_similarity[CompositePixelChannel]+=cos(delta);
1830 p+=(ptrdiff_t) GetPixelChannels(image);
1831 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1833#if defined(MAGICKCORE_OPENMP_SUPPORT)
1834 #pragma omp critical (MagickCore_GetPHASESimilarity)
1841 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1843 PixelChannel channel = GetPixelChannelChannel(image,j);
1844 PixelTrait traits = GetPixelChannelTraits(image,channel);
1845 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1847 if (((traits & UpdatePixelTrait) == 0) ||
1848 ((reconstruct_traits & UpdatePixelTrait) == 0))
1850 similarity[j]+=channel_similarity[j];
1852 similarity[CompositePixelChannel]+=
1853 channel_similarity[CompositePixelChannel];
1856 reconstruct_view=DestroyCacheView(reconstruct_view);
1857 image_view=DestroyCacheView(image_view);
1858 area=MagickSafeReciprocal(area);
1859 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1861 PixelChannel channel = GetPixelChannelChannel(image,k);
1862 PixelTrait traits = GetPixelChannelTraits(image,channel);
1863 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1865 if (((traits & UpdatePixelTrait) == 0) ||
1866 ((reconstruct_traits & UpdatePixelTrait) == 0))
1868 similarity[k]*=area;
1870 similarity[CompositePixelChannel]*=area;
1871 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1875static MagickBooleanType GetPSNRSimilarity(
const Image *image,
1876 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1879 status = MagickTrue;
1887 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
1888 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1890 PixelChannel channel = GetPixelChannelChannel(image,i);
1891 PixelTrait traits = GetPixelChannelTraits(image,channel);
1892 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1894 if (((traits & UpdatePixelTrait) == 0) ||
1895 ((reconstruct_traits & UpdatePixelTrait) == 0))
1897 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
1898 similarity[i]))/MagickSafePSNRRecipicol(10.0);
1900 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
1901 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
1902 MagickSafePSNRRecipicol(10.0);
1906static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1907 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1909 ChannelPerceptualHash
1922 channel_phash=GetImagePerceptualHash(image,exception);
1923 if (channel_phash == (ChannelPerceptualHash *) NULL)
1924 return(MagickFalse);
1925 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1926 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1928 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1930 return(MagickFalse);
1932 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1940 PixelChannel channel = GetPixelChannelChannel(image,i);
1941 PixelTrait traits = GetPixelChannelTraits(image,channel);
1942 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1944 if (((traits & UpdatePixelTrait) == 0) ||
1945 ((reconstruct_traits & UpdatePixelTrait) == 0))
1947 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1956 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1961 alpha=channel_phash[i].phash[j][k];
1962 beta=reconstruct_phash[i].phash[j][k];
1964 if (IsNaN(error) != 0)
1966 difference+=error*error;
1969 similarity[i]+=difference;
1970 similarity[CompositePixelChannel]+=difference;
1972 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1973 artifact=GetImageArtifact(image,
"phash:normalize");
1974 if (IsStringTrue(artifact) != MagickFalse)
1976 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1978 PixelChannel channel = GetPixelChannelChannel(image,i);
1979 PixelTrait traits = GetPixelChannelTraits(image,channel);
1980 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1982 if (((traits & UpdatePixelTrait) == 0) ||
1983 ((reconstruct_traits & UpdatePixelTrait) == 0))
1985 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
1987 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
1988 channel_phash[0].number_colorspaces);
1993 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1995 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
1999static MagickBooleanType GetRMSESimilarity(
const Image *image,
2000 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2002#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2005 status = MagickTrue;
2013 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2014 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2016 PixelChannel channel = GetPixelChannelChannel(image,i);
2017 PixelTrait traits = GetPixelChannelTraits(image,channel);
2018 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2020 if (((traits & UpdatePixelTrait) == 0) ||
2021 ((reconstruct_traits & UpdatePixelTrait) == 0))
2023 similarity[i]=RMSESquareRoot(similarity[i]);
2025 similarity[CompositePixelChannel]=RMSESquareRoot(
2026 similarity[CompositePixelChannel]);
2030static MagickBooleanType GetSSIMSimularity(
const Image *image,
2031 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2033#define SSIMRadius 5.0
2034#define SSIMSigma 1.5
2044 geometry[MagickPathExtent];
2060 status = MagickTrue;
2074 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2075 if (artifact != (
const char *) NULL)
2076 radius=StringToDouble(artifact,(
char **) NULL);
2078 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2079 if (artifact != (
const char *) NULL)
2080 sigma=StringToDouble(artifact,(
char **) NULL);
2081 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.20gx%.20g",
2083 kernel_info=AcquireKernelInfo(geometry,exception);
2084 if (kernel_info == (KernelInfo *) NULL)
2085 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2087 c1=pow(SSIMK1*SSIML,2.0);
2088 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2089 if (artifact != (
const char *) NULL)
2090 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2091 c2=pow(SSIMK2*SSIML,2.0);
2092 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2093 if (artifact != (
const char *) NULL)
2094 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2095 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2096 image_view=AcquireVirtualCacheView(image,exception);
2097 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2098#if defined(MAGICKCORE_OPENMP_SUPPORT)
2099 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2100 magick_number_threads(image,reconstruct_image,rows,1)
2102 for (y=0; y < (ssize_t) rows; y++)
2110 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2116 if (status == MagickFalse)
2118 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2119 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2120 kernel_info->height,exception);
2121 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2122 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2123 kernel_info->height,exception);
2124 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2129 for (x=0; x < (ssize_t) columns; x++)
2132 *magick_restrict reconstruct,
2133 *magick_restrict test;
2136 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2137 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2138 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2139 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2140 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2148 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2149 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2151 p+=(ptrdiff_t) GetPixelChannels(image);
2152 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2155 k=kernel_info->values;
2158 for (v=0; v < (ssize_t) kernel_info->height; v++)
2163 for (u=0; u < (ssize_t) kernel_info->width; u++)
2165 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2171 PixelChannel channel = GetPixelChannelChannel(image,i);
2172 PixelTrait traits = GetPixelChannelTraits(image,channel);
2173 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2174 reconstruct_image,channel);
2175 if (((traits & UpdatePixelTrait) == 0) ||
2176 ((reconstruct_traits & UpdatePixelTrait) == 0))
2178 x_pixel=QuantumScale*(double) test[i];
2179 x_pixel_mu[i]+=(*k)*x_pixel;
2180 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2181 y_pixel=QuantumScale*(double)
2182 GetPixelChannel(reconstruct_image,channel,reconstruct);
2183 y_pixel_mu[i]+=(*k)*y_pixel;
2184 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2185 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2188 test+=(ptrdiff_t) GetPixelChannels(image);
2189 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2191 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2192 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2194 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2199 x_pixel_sigmas_squared,
2203 y_pixel_sigmas_squared;
2205 PixelChannel channel = GetPixelChannelChannel(image,i);
2206 PixelTrait traits = GetPixelChannelTraits(image,channel);
2207 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2208 reconstruct_image,channel);
2209 if (((traits & UpdatePixelTrait) == 0) ||
2210 ((reconstruct_traits & UpdatePixelTrait) == 0))
2212 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2213 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2214 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2215 xy_sigmas=xy_sigma[i]-xy_mu;
2216 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2217 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2218 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2219 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2220 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2221 channel_similarity[i]+=ssim;
2222 channel_similarity[CompositePixelChannel]+=ssim;
2224 p+=(ptrdiff_t) GetPixelChannels(image);
2225 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2228#if defined(MAGICKCORE_OPENMP_SUPPORT)
2229 #pragma omp critical (MagickCore_GetSSIMSimularity)
2236 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2238 PixelChannel channel = GetPixelChannelChannel(image,j);
2239 PixelTrait traits = GetPixelChannelTraits(image,channel);
2240 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2242 if (((traits & UpdatePixelTrait) == 0) ||
2243 ((reconstruct_traits & UpdatePixelTrait) == 0))
2245 similarity[j]+=channel_similarity[j];
2247 similarity[CompositePixelChannel]+=
2248 channel_similarity[CompositePixelChannel];
2251 image_view=DestroyCacheView(image_view);
2252 reconstruct_view=DestroyCacheView(reconstruct_view);
2253 area=MagickSafeReciprocal(area);
2254 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2256 PixelChannel channel = GetPixelChannelChannel(image,l);
2257 PixelTrait traits = GetPixelChannelTraits(image,channel);
2258 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2260 if (((traits & UpdatePixelTrait) == 0) ||
2261 ((reconstruct_traits & UpdatePixelTrait) == 0))
2263 similarity[l]*=area;
2265 similarity[CompositePixelChannel]*=area;
2266 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2267 kernel_info=DestroyKernelInfo(kernel_info);
2271static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2272 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2275 status = MagickTrue;
2283 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2284 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2286 PixelChannel channel = GetPixelChannelChannel(image,i);
2287 PixelTrait traits = GetPixelChannelTraits(image,channel);
2288 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2290 if (((traits & UpdatePixelTrait) == 0) ||
2291 ((reconstruct_traits & UpdatePixelTrait) == 0))
2293 similarity[i]=(1.0-similarity[i])/2.0;
2295 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2299MagickExport MagickBooleanType GetImageDistortion(Image *image,
2300 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2301 ExceptionInfo *exception)
2303#define CompareMetricNotSupportedException "metric not supported"
2306 *channel_similarity;
2309 status = MagickTrue;
2314 assert(image != (Image *) NULL);
2315 assert(image->signature == MagickCoreSignature);
2316 assert(reconstruct_image != (
const Image *) NULL);
2317 assert(reconstruct_image->signature == MagickCoreSignature);
2318 assert(distortion != (
double *) NULL);
2319 if (IsEventLogging() != MagickFalse)
2320 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2325 length=MaxPixelChannels+1UL;
2326 channel_similarity=(
double *) AcquireQuantumMemory(length,
2327 sizeof(*channel_similarity));
2328 if (channel_similarity == (
double *) NULL)
2329 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2330 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2333 case AbsoluteErrorMetric:
2335 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2339 case DotProductCorrelationErrorMetric:
2341 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2345 case FuzzErrorMetric:
2347 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2351 case MeanAbsoluteErrorMetric:
2353 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2357 case MeanErrorPerPixelErrorMetric:
2359 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2363 case MeanSquaredErrorMetric:
2365 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2369 case NormalizedCrossCorrelationErrorMetric:
2371 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2375 case PeakAbsoluteErrorMetric:
2377 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2381 case PeakSignalToNoiseRatioErrorMetric:
2383 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2387 case PerceptualHashErrorMetric:
2389 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2393 case PhaseCorrelationErrorMetric:
2395 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2399 case PixelDifferenceCountErrorMetric:
2401 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2405 case RootMeanSquaredErrorMetric:
2406 case UndefinedErrorMetric:
2409 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2413 case StructuralDissimilarityErrorMetric:
2415 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2419 case StructuralSimilarityErrorMetric:
2421 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2426 *distortion=channel_similarity[CompositePixelChannel];
2429 case DotProductCorrelationErrorMetric:
2430 case NormalizedCrossCorrelationErrorMetric:
2431 case PhaseCorrelationErrorMetric:
2432 case StructuralSimilarityErrorMetric:
2434 *distortion=(1.0-(*distortion))/2.0;
2439 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2440 if (fabs(*distortion) < MagickEpsilon)
2442 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2478MagickExport
double *GetImageDistortions(Image *image,
2479 const Image *reconstruct_image,
const MetricType metric,
2480 ExceptionInfo *exception)
2484 *channel_similarity;
2487 status = MagickTrue;
2495 assert(image != (Image *) NULL);
2496 assert(image->signature == MagickCoreSignature);
2497 assert(reconstruct_image != (
const Image *) NULL);
2498 assert(reconstruct_image->signature == MagickCoreSignature);
2499 if (IsEventLogging() != MagickFalse)
2500 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2504 length=MaxPixelChannels+1UL;
2505 channel_similarity=(
double *) AcquireQuantumMemory(length,
2506 sizeof(*channel_similarity));
2507 if (channel_similarity == (
double *) NULL)
2508 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2509 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2512 case AbsoluteErrorMetric:
2514 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2518 case DotProductCorrelationErrorMetric:
2520 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2524 case FuzzErrorMetric:
2526 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2530 case MeanAbsoluteErrorMetric:
2532 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2536 case MeanErrorPerPixelErrorMetric:
2538 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2542 case MeanSquaredErrorMetric:
2544 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2548 case NormalizedCrossCorrelationErrorMetric:
2550 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2554 case PeakAbsoluteErrorMetric:
2556 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2560 case PeakSignalToNoiseRatioErrorMetric:
2562 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2566 case PerceptualHashErrorMetric:
2568 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2572 case PhaseCorrelationErrorMetric:
2574 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2578 case PixelDifferenceCountErrorMetric:
2580 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2584 case RootMeanSquaredErrorMetric:
2585 case UndefinedErrorMetric:
2588 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2592 case StructuralDissimilarityErrorMetric:
2594 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2598 case StructuralSimilarityErrorMetric:
2600 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2605 if (status == MagickFalse)
2607 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2608 return((
double *) NULL);
2610 distortion=channel_similarity;
2613 case DotProductCorrelationErrorMetric:
2614 case NormalizedCrossCorrelationErrorMetric:
2615 case PhaseCorrelationErrorMetric:
2616 case StructuralSimilarityErrorMetric:
2618 for (i=0; i <= MaxPixelChannels; i++)
2619 distortion[i]=(1.0-distortion[i])/2.0;
2624 for (i=0; i <= MaxPixelChannels; i++)
2625 if (fabs(distortion[i]) < MagickEpsilon)
2627 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2628 distortion[CompositePixelChannel]);
2660MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2661 const Image *reconstruct_image,ExceptionInfo *exception)
2674 assert(image != (Image *) NULL);
2675 assert(image->signature == MagickCoreSignature);
2676 assert(reconstruct_image != (
const Image *) NULL);
2677 assert(reconstruct_image->signature == MagickCoreSignature);
2678 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2679 image_view=AcquireVirtualCacheView(image,exception);
2680 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2681 for (y=0; y < (ssize_t) rows; y++)
2690 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2691 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2692 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2694 for (x=0; x < (ssize_t) columns; x++)
2699 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2704 PixelChannel channel = GetPixelChannelChannel(image,i);
2705 PixelTrait traits = GetPixelChannelTraits(image,channel);
2706 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2708 if (((traits & UpdatePixelTrait) == 0) ||
2709 ((reconstruct_traits & UpdatePixelTrait) == 0))
2711 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2713 if (distance >= MagickEpsilon)
2716 if (i < (ssize_t) GetPixelChannels(image))
2718 p+=(ptrdiff_t) GetPixelChannels(image);
2719 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2721 if (x < (ssize_t) columns)
2724 reconstruct_view=DestroyCacheView(reconstruct_view);
2725 image_view=DestroyCacheView(image_view);
2726 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2778MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2779 const Image *reconstruct_image,ExceptionInfo *exception)
2782 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2787 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2789 if (status == MagickFalse)
2790 return(MagickFalse);
2791 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2792 MagickTrue : MagickFalse;
2839#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2840static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2841 const Image *beta_image,ExceptionInfo *exception)
2844 *alpha_fft = (Image *) NULL,
2845 *beta_fft = (Image *) NULL,
2846 *complex_conjugate = (Image *) NULL,
2847 *complex_multiplication = (Image *) NULL,
2848 *cross_correlation = (Image *) NULL,
2849 *temp_image = (Image *) NULL;
2854 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2855 if (temp_image == (Image *) NULL)
2856 return((Image *) NULL);
2857 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2858 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2859 temp_image=DestroyImageList(temp_image);
2860 if (beta_fft == (Image *) NULL)
2861 return((Image *) NULL);
2865 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2866 beta_fft=DestroyImageList(beta_fft);
2867 if (complex_conjugate == (Image *) NULL)
2868 return((Image *) NULL);
2872 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2873 if (temp_image == (Image *) NULL)
2875 complex_conjugate=DestroyImageList(complex_conjugate);
2876 return((Image *) NULL);
2878 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2879 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2880 temp_image=DestroyImageList(temp_image);
2881 if (alpha_fft == (Image *) NULL)
2883 complex_conjugate=DestroyImageList(complex_conjugate);
2884 return((Image *) NULL);
2889 DisableCompositeClampUnlessSpecified(complex_conjugate);
2890 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2891 AppendImageToList(&complex_conjugate,alpha_fft);
2892 complex_multiplication=ComplexImages(complex_conjugate,
2893 MultiplyComplexOperator,exception);
2894 complex_conjugate=DestroyImageList(complex_conjugate);
2895 if (complex_multiplication == (Image *) NULL)
2896 return((Image *) NULL);
2900 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2901 complex_multiplication->next,MagickFalse,exception);
2902 complex_multiplication=DestroyImageList(complex_multiplication);
2903 return(cross_correlation);
2906static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2907 ExceptionInfo *exception)
2915 kernel_info=AcquireKernelInfo(kernel,exception);
2916 if (kernel_info == (KernelInfo *) NULL)
2917 return((Image *) NULL);
2918 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2920 kernel_info=DestroyKernelInfo(kernel_info);
2921 return(derivative_image);
2924static Image *SIMDivideImage(
const Image *numerator_image,
2925 const Image *denominator_image,ExceptionInfo *exception)
2935 status = MagickTrue;
2943 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2944 if (divide_image == (Image *) NULL)
2945 return(divide_image);
2946 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2947 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2948#if defined(MAGICKCORE_OPENMP_SUPPORT)
2949 #pragma omp parallel for schedule(static) shared(status) \
2950 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
2952 for (y=0; y < (ssize_t) divide_image->rows; y++)
2963 if (status == MagickFalse)
2965 p=GetCacheViewVirtualPixels(denominator_view,0,y,
2966 denominator_image->columns,1,exception);
2967 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
2969 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2974 for (x=0; x < (ssize_t) divide_image->columns; x++)
2979 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
2981 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
2982 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
2983 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
2985 if (((traits & UpdatePixelTrait) == 0) ||
2986 ((denominator_traits & UpdatePixelTrait) == 0))
2988 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
2989 (
double) GetPixelChannel(denominator_image,channel,p)));
2991 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
2992 q+=(ptrdiff_t) GetPixelChannels(divide_image);
2994 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
2997 denominator_view=DestroyCacheView(denominator_view);
2998 numerator_view=DestroyCacheView(numerator_view);
2999 if (status == MagickFalse)
3000 divide_image=DestroyImage(divide_image);
3001 return(divide_image);
3004static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3005 const Image *source_image,ExceptionInfo *exception)
3014 divide_image=SIMDivideImage(image,magnitude_image,exception);
3015 if (divide_image == (Image *) NULL)
3016 return((Image *) NULL);
3017 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3018 ÷_image->background_color);
3019 SetGeometry(source_image,&geometry);
3020 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3021 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3022 result_image=ExtentImage(divide_image,&geometry,exception);
3023 divide_image=DestroyImage(divide_image);
3024 return(result_image);
3027static MagickBooleanType SIMFilterImageNaNs(Image *image,
3028 ExceptionInfo *exception)
3034 status = MagickTrue;
3042 image_view=AcquireAuthenticCacheView(image,exception);
3043#if defined(MAGICKCORE_OPENMP_SUPPORT)
3044 #pragma omp parallel for schedule(static) shared(status) \
3045 magick_number_threads(image,image,image->rows,1)
3047 for (y=0; y < (ssize_t) image->rows; y++)
3055 if (status == MagickFalse)
3057 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3058 if (q == (Quantum *) NULL)
3063 for (x=0; x < (ssize_t) image->columns; x++)
3068 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3070 PixelChannel channel = GetPixelChannelChannel(image,i);
3071 PixelTrait traits = GetPixelChannelTraits(image,channel);
3072 if ((traits & UpdatePixelTrait) == 0)
3074 if (IsNaN((
double) q[i]) != 0)
3077 q+=(ptrdiff_t) GetPixelChannels(image);
3079 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3082 image_view=DestroyCacheView(image_view);
3086static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3095 status = MagickTrue;
3103 square_image=CloneImage(image,0,0,MagickTrue,exception);
3104 if (square_image == (Image *) NULL)
3105 return(square_image);
3106 image_view=AcquireAuthenticCacheView(square_image,exception);
3107#if defined(MAGICKCORE_OPENMP_SUPPORT)
3108 #pragma omp parallel for schedule(static) shared(status) \
3109 magick_number_threads(square_image,square_image,square_image->rows,1)
3111 for (y=0; y < (ssize_t) square_image->rows; y++)
3119 if (status == MagickFalse)
3121 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3123 if (q == (Quantum *) NULL)
3128 for (x=0; x < (ssize_t) square_image->columns; x++)
3133 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3135 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3136 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3137 if ((traits & UpdatePixelTrait) == 0)
3139 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3141 q+=(ptrdiff_t) GetPixelChannels(square_image);
3143 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3146 image_view=DestroyCacheView(image_view);
3147 if (status == MagickFalse)
3148 square_image=DestroyImage(square_image);
3149 return(square_image);
3152static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3153 ExceptionInfo *exception)
3161 status = MagickTrue;
3163 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3164 xsq_image=SIMSquareImage(alpha_image,exception);
3165 if (xsq_image == (Image *) NULL)
3166 return((Image *) NULL);
3167 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3168 ysq_image=SIMSquareImage(beta_image,exception);
3169 if (ysq_image == (Image *) NULL)
3171 xsq_image=DestroyImage(xsq_image);
3172 return((Image *) NULL);
3174 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3176 magnitude_image=xsq_image;
3177 ysq_image=DestroyImage(ysq_image);
3178 if (status == MagickFalse)
3180 magnitude_image=DestroyImage(magnitude_image);
3181 return((Image *) NULL);
3183 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3184 if (status == MagickFalse)
3186 magnitude_image=DestroyImage(magnitude_image);
3187 return (Image *) NULL;
3189 return(magnitude_image);
3192static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3193 RectangleInfo *offset,ExceptionInfo *exception)
3212 status = MagickTrue;
3215 maxima_info = { -MagickMaximumValue, 0, 0 };
3223 image_view=AcquireVirtualCacheView(image,exception);
3224 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3226 if (q != (
const Quantum *) NULL)
3227 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3228#if defined(MAGICKCORE_OPENMP_SUPPORT)
3229 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3230 magick_number_threads(image,image,image->rows,1)
3232 for (y=0; y < (ssize_t) image->rows; y++)
3238 channel_maxima = { -MagickMaximumValue, 0, 0 };
3243 if (status == MagickFalse)
3245 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3246 if (p == (
const Quantum *) NULL)
3251 channel_maxima=maxima_info;
3252 for (x=0; x < (ssize_t) image->columns; x++)
3257 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3262 PixelChannel channel = GetPixelChannelChannel(image,i);
3263 PixelTrait traits = GetPixelChannelTraits(image,channel);
3264 if ((traits & UpdatePixelTrait) == 0)
3266 pixel=(double) p[i];
3267 if (IsNaN(pixel) != 0)
3269 if (pixel > channel_maxima.maxima)
3271 channel_maxima.maxima=(double) p[i];
3276 p+=(ptrdiff_t) GetPixelChannels(image);
3278#if defined(MAGICKCORE_OPENMP_SUPPORT)
3279 #pragma omp critical (MagickCore_SIMMaximaImage)
3281 if (channel_maxima.maxima > maxima_info.maxima)
3282 maxima_info=channel_maxima;
3284 image_view=DestroyCacheView(image_view);
3285 *maxima=maxima_info.maxima;
3286 offset->x=maxima_info.x;
3287 offset->y=maxima_info.y;
3291static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3292 RectangleInfo *offset,ExceptionInfo *exception)
3311 status = MagickTrue;
3314 minima_info = { MagickMaximumValue, 0, 0 };
3322 image_view=AcquireVirtualCacheView(image,exception);
3323 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3325 if (q != (
const Quantum *) NULL)
3326 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3327#if defined(MAGICKCORE_OPENMP_SUPPORT)
3328 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3329 magick_number_threads(image,image,image->rows,1)
3331 for (y=0; y < (ssize_t) image->rows; y++)
3337 channel_minima = { MagickMaximumValue, 0, 0 };
3342 if (status == MagickFalse)
3344 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3345 if (p == (
const Quantum *) NULL)
3350 channel_minima=minima_info;
3351 for (x=0; x < (ssize_t) image->columns; x++)
3356 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3361 PixelChannel channel = GetPixelChannelChannel(image,i);
3362 PixelTrait traits = GetPixelChannelTraits(image,channel);
3363 if ((traits & UpdatePixelTrait) == 0)
3365 pixel=(double) p[i];
3366 if (IsNaN(pixel) != 0)
3368 if (pixel < channel_minima.minima)
3370 channel_minima.minima=pixel;
3375 p+=(ptrdiff_t) GetPixelChannels(image);
3377#if defined(MAGICKCORE_OPENMP_SUPPORT)
3378 #pragma omp critical (MagickCore_SIMMinimaImage)
3380 if (channel_minima.minima < minima_info.minima)
3381 minima_info=channel_minima;
3383 image_view=DestroyCacheView(image_view);
3384 *minima=minima_info.minima;
3385 offset->x=minima_info.x;
3386 offset->y=minima_info.y;
3390static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3391 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3397 status = MagickTrue;
3405 image_view=AcquireAuthenticCacheView(image,exception);
3406#if defined(MAGICKCORE_OPENMP_SUPPORT)
3407 #pragma omp parallel for schedule(static) shared(status) \
3408 magick_number_threads(image,image,image->rows,1)
3410 for (y=0; y < (ssize_t) image->rows; y++)
3418 if (status == MagickFalse)
3420 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3421 if (q == (Quantum *) NULL)
3426 for (x=0; x < (ssize_t) image->columns; x++)
3431 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3433 PixelChannel channel = GetPixelChannelChannel(image,i);
3434 PixelTrait traits = GetPixelChannelTraits(image,channel);
3435 if ((traits & UpdatePixelTrait) == 0)
3437 if (channel_statistics != (
const ChannelStatistics *) NULL)
3438 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3439 channel_statistics[channel].standard_deviation);
3441 q[i]=(Quantum) (factor*q[i]);
3443 q+=(ptrdiff_t) GetPixelChannels(image);
3445 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3448 image_view=DestroyCacheView(image_view);
3452static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3453 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3456 *alpha_fft = (Image *) NULL,
3457 *beta_fft = (Image *) NULL,
3458 *complex_multiplication = (Image *) NULL,
3459 *cross_correlation = (Image *) NULL;
3464 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3465 if (beta_fft == NULL)
3466 return((Image *) NULL);
3467 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3468 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3469 if (beta_fft == NULL)
3470 return((Image *) NULL);
3474 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3475 if (alpha_fft == (Image *) NULL)
3477 beta_fft=DestroyImageList(beta_fft);
3478 return((Image *) NULL);
3480 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3481 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3482 if (alpha_fft == (Image *) NULL)
3484 beta_fft=DestroyImageList(beta_fft);
3485 return((Image *) NULL);
3490 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3491 if (beta_fft == (Image *) NULL)
3493 alpha_fft=DestroyImageList(alpha_fft);
3494 return((Image *) NULL);
3499 AppendImageToList(&beta_fft,alpha_fft);
3500 DisableCompositeClampUnlessSpecified(beta_fft);
3501 DisableCompositeClampUnlessSpecified(beta_fft->next);
3502 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3504 beta_fft=DestroyImageList(beta_fft);
3505 if (complex_multiplication == (Image *) NULL)
3506 return((Image *) NULL);
3510 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3511 magnitude_image,0,0,exception);
3515 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3516 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3517 complex_multiplication->next,MagickFalse,exception);
3518 complex_multiplication=DestroyImageList(complex_multiplication);
3519 return(cross_correlation);
3522static MagickBooleanType SIMSetImageMean(Image *image,
3523 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3529 status = MagickTrue;
3537 image_view=AcquireAuthenticCacheView(image,exception);
3538#if defined(MAGICKCORE_OPENMP_SUPPORT)
3539 #pragma omp parallel for schedule(static) shared(status) \
3540 magick_number_threads(image,image,image->rows,1)
3542 for (y=0; y < (ssize_t) image->rows; y++)
3550 if (status == MagickFalse)
3552 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3553 if (q == (Quantum *) NULL)
3558 for (x=0; x < (ssize_t) image->columns; x++)
3563 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3565 PixelChannel channel = GetPixelChannelChannel(image,i);
3566 PixelTrait traits = GetPixelChannelTraits(image,channel);
3567 if ((traits & UpdatePixelTrait) == 0)
3569 q[i]=(Quantum) channel_statistics[channel].mean;
3571 q+=(ptrdiff_t) GetPixelChannels(image);
3573 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3576 image_view=DestroyCacheView(image_view);
3580static Image *SIMSubtractImageMean(
const Image *alpha_image,
3581 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3582 ExceptionInfo *exception)
3592 status = MagickTrue;
3600 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3601 MagickTrue,exception);
3602 if (subtract_image == (Image *) NULL)
3603 return(subtract_image);
3604 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3605 beta_view=AcquireVirtualCacheView(beta_image,exception);
3606#if defined(MAGICKCORE_OPENMP_SUPPORT)
3607 #pragma omp parallel for schedule(static) shared(status) \
3608 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3610 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3621 if (status == MagickFalse)
3623 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3624 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3626 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3631 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3636 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3638 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3639 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3640 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3641 if (((traits & UpdatePixelTrait) == 0) ||
3642 ((beta_traits & UpdatePixelTrait) == 0))
3644 if ((x >= (ssize_t) beta_image->columns) ||
3645 (y >= (ssize_t) beta_image->rows))
3648 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3649 channel_statistics[channel].mean);
3651 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3652 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3654 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3657 beta_view=DestroyCacheView(beta_view);
3658 image_view=DestroyCacheView(image_view);
3659 if (status == MagickFalse)
3660 subtract_image=DestroyImage(subtract_image);
3661 return(subtract_image);
3664static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3665 ExceptionInfo *exception)
3674 status = MagickTrue;
3682 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3683 MagickTrue,exception);
3684 if (unity_image == (Image *) NULL)
3685 return(unity_image);
3686 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3687 return(DestroyImage(unity_image));
3688 image_view=AcquireAuthenticCacheView(unity_image,exception);
3689#if defined(MAGICKCORE_OPENMP_SUPPORT)
3690 #pragma omp parallel for schedule(static) shared(status) \
3691 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3693 for (y=0; y < (ssize_t) unity_image->rows; y++)
3701 if (status == MagickFalse)
3703 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3705 if (q == (Quantum *) NULL)
3710 for (x=0; x < (ssize_t) unity_image->columns; x++)
3715 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3717 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3718 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3719 if ((traits & UpdatePixelTrait) == 0)
3721 if ((x >= (ssize_t) beta_image->columns) ||
3722 (y >= (ssize_t) beta_image->rows))
3727 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3729 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3732 image_view=DestroyCacheView(image_view);
3733 if (status == MagickFalse)
3734 unity_image=DestroyImage(unity_image);
3735 return(unity_image);
3738static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3739 ExceptionInfo *exception)
3749 status = MagickTrue;
3757 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3758 if (variance_image == (Image *) NULL)
3759 return(variance_image);
3760 image_view=AcquireAuthenticCacheView(variance_image,exception);
3761 beta_view=AcquireVirtualCacheView(beta_image,exception);
3762#if defined(MAGICKCORE_OPENMP_SUPPORT)
3763 #pragma omp parallel for schedule(static) shared(status) \
3764 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3766 for (y=0; y < (ssize_t) variance_image->rows; y++)
3777 if (status == MagickFalse)
3779 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3781 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3783 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3788 for (x=0; x < (ssize_t) variance_image->columns; x++)
3793 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3798 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3799 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3800 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3801 if (((traits & UpdatePixelTrait) == 0) ||
3802 ((beta_traits & UpdatePixelTrait) == 0))
3804 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3805 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3806 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3808 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3809 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3811 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3814 beta_view=DestroyCacheView(beta_view);
3815 image_view=DestroyCacheView(image_view);
3816 if (status == MagickFalse)
3817 variance_image=DestroyImage(variance_image);
3818 return(variance_image);
3821static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3822 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3824#define ThrowDPCSimilarityException() \
3826 if (dot_product_image != (Image *) NULL) \
3827 dot_product_image=DestroyImage(dot_product_image); \
3828 if (magnitude_image != (Image *) NULL) \
3829 magnitude_image=DestroyImage(magnitude_image); \
3830 if (reconstruct_image != (Image *) NULL) \
3831 reconstruct_image=DestroyImage(reconstruct_image); \
3832 if (rx_image != (Image *) NULL) \
3833 rx_image=DestroyImage(rx_image); \
3834 if (ry_image != (Image *) NULL) \
3835 ry_image=DestroyImage(ry_image); \
3836 if (target_image != (Image *) NULL) \
3837 target_image=DestroyImage(target_image); \
3838 if (threshold_image != (Image *) NULL) \
3839 threshold_image=DestroyImage(threshold_image); \
3840 if (trx_image != (Image *) NULL) \
3841 trx_image=DestroyImage(trx_image); \
3842 if (try_image != (Image *) NULL) \
3843 try_image=DestroyImage(try_image); \
3844 if (tx_image != (Image *) NULL) \
3845 tx_image=DestroyImage(tx_image); \
3846 if (ty_image != (Image *) NULL) \
3847 ty_image=DestroyImage(ty_image); \
3848 return((Image *) NULL); \
3855 standard_deviation = 0.0;
3858 *dot_product_image = (Image *) NULL,
3859 *magnitude_image = (Image *) NULL,
3860 *reconstruct_image = (Image *) NULL,
3861 *rx_image = (Image *) NULL,
3862 *ry_image = (Image *) NULL,
3863 *trx_image = (Image *) NULL,
3864 *target_image = (Image *) NULL,
3865 *threshold_image = (Image *) NULL,
3866 *try_image = (Image *) NULL,
3867 *tx_image = (Image *) NULL,
3868 *ty_image = (Image *) NULL;
3871 status = MagickTrue;
3879 target_image=CloneImage(image,0,0,MagickTrue,exception);
3880 if (target_image == (Image *) NULL)
3881 return((Image *) NULL);
3885 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3886 if (reconstruct_image == (Image *) NULL)
3887 ThrowDPCSimilarityException();
3891 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3893 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3894 if (rx_image == (Image *) NULL)
3895 ThrowDPCSimilarityException();
3896 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3897 reconstruct_image=DestroyImage(reconstruct_image);
3898 if (ry_image == (Image *) NULL)
3899 ThrowDPCSimilarityException();
3903 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3904 if (magnitude_image == (Image *) NULL)
3905 ThrowDPCSimilarityException();
3909 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3910 if (threshold_image == (Image *) NULL)
3911 ThrowDPCSimilarityException();
3912 status=BilevelImage(threshold_image,0.0,exception);
3913 if (status == MagickFalse)
3914 ThrowDPCSimilarityException();
3915 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3916 threshold_image=DestroyImage(threshold_image);
3917 if (status == MagickFalse)
3918 ThrowDPCSimilarityException();
3919 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
3920 reconstruct->rows)+QuantumScale;
3924 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3925 rx_image=DestroyImage(rx_image);
3926 if (trx_image == (Image *) NULL)
3927 ThrowDPCSimilarityException();
3929 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3930 magnitude_image=DestroyImage(magnitude_image);
3931 ry_image=DestroyImage(ry_image);
3932 if (try_image == (Image *) NULL)
3933 ThrowDPCSimilarityException();
3938 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
3940 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
3941 if (tx_image == (Image *) NULL)
3942 ThrowDPCSimilarityException();
3943 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
3944 target_image=DestroyImage(target_image);
3945 if (ty_image == (Image *) NULL)
3946 ThrowDPCSimilarityException();
3950 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
3951 if (magnitude_image == (Image *) NULL)
3952 ThrowDPCSimilarityException();
3956 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
3957 tx_image=DestroyImage(tx_image);
3958 if (trx_image == (Image *) NULL)
3959 ThrowDPCSimilarityException();
3961 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
3962 ty_image=DestroyImage(ty_image);
3963 magnitude_image=DestroyImage(magnitude_image);
3964 if (try_image == (Image *) NULL)
3965 ThrowDPCSimilarityException();
3970 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
3971 rx_image=DestroyImage(rx_image);
3972 tx_image=DestroyImage(tx_image);
3973 if (trx_image == (Image *) NULL)
3974 ThrowDPCSimilarityException();
3975 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
3976 ry_image=DestroyImage(ry_image);
3977 ty_image=DestroyImage(ty_image);
3978 if (try_image == (Image *) NULL)
3979 ThrowDPCSimilarityException();
3983 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
3984 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
3986 try_image=DestroyImage(try_image);
3987 if (status == MagickFalse)
3988 ThrowDPCSimilarityException();
3989 status=SIMMultiplyImage(trx_image,edge_factor,
3990 (
const ChannelStatistics *) NULL,exception);
3991 if (status == MagickFalse)
3992 ThrowDPCSimilarityException();
3996 SetGeometry(image,&geometry);
3997 geometry.width=image->columns;
3998 geometry.height=image->rows;
3999 (void) ResetImagePage(trx_image,
"0x0+0+0");
4000 dot_product_image=CropImage(trx_image,&geometry,exception);
4001 trx_image=DestroyImage(trx_image);
4002 if (dot_product_image == (Image *) NULL)
4003 ThrowDPCSimilarityException();
4004 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4008 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4010 if (status == MagickFalse)
4011 ThrowDPCSimilarityException();
4012 dot_product_image->depth=32;
4013 dot_product_image->colorspace=GRAYColorspace;
4014 dot_product_image->alpha_trait=UndefinedPixelTrait;
4015 status=SIMFilterImageNaNs(dot_product_image,exception);
4016 if (status == MagickFalse)
4017 ThrowDPCSimilarityException();
4018 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4019 if (status == MagickFalse)
4020 ThrowDPCSimilarityException();
4021 if ((QuantumScale*maxima) > 1.0)
4023 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4024 (
const ChannelStatistics *) NULL,exception);
4025 maxima=(double) QuantumRange;
4027 *similarity_metric=QuantumScale*maxima;
4028 return(dot_product_image);
4031static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4032 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4034#define ThrowMSESimilarityException() \
4036 if (alpha_image != (Image *) NULL) \
4037 alpha_image=DestroyImage(alpha_image); \
4038 if (beta_image != (Image *) NULL) \
4039 beta_image=DestroyImage(beta_image); \
4040 if (channel_statistics != (ChannelStatistics *) NULL) \
4041 channel_statistics=(ChannelStatistics *) \
4042 RelinquishMagickMemory(channel_statistics); \
4043 if (mean_image != (Image *) NULL) \
4044 mean_image=DestroyImage(mean_image); \
4045 if (mse_image != (Image *) NULL) \
4046 mse_image=DestroyImage(mse_image); \
4047 if (reconstruct_image != (Image *) NULL) \
4048 reconstruct_image=DestroyImage(reconstruct_image); \
4049 if (sum_image != (Image *) NULL) \
4050 sum_image=DestroyImage(sum_image); \
4051 if (alpha_image != (Image *) NULL) \
4052 alpha_image=DestroyImage(alpha_image); \
4053 return((Image *) NULL); \
4057 *channel_statistics = (ChannelStatistics *) NULL;
4063 *alpha_image = (Image *) NULL,
4064 *beta_image = (Image *) NULL,
4065 *mean_image = (Image *) NULL,
4066 *mse_image = (Image *) NULL,
4067 *reconstruct_image = (Image *) NULL,
4068 *sum_image = (Image *) NULL,
4069 *target_image = (Image *) NULL;
4072 status = MagickTrue;
4080 target_image=SIMSquareImage(image,exception);
4081 if (target_image == (Image *) NULL)
4082 ThrowMSESimilarityException();
4083 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4084 if (reconstruct_image == (Image *) NULL)
4085 ThrowMSESimilarityException();
4089 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4091 target_image=DestroyImage(target_image);
4092 if (alpha_image == (Image *) NULL)
4093 ThrowMSESimilarityException();
4094 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4095 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4096 if (status == MagickFalse)
4097 ThrowMSESimilarityException();
4101 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4102 MagickTrue,0,0,exception);
4103 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4104 if (beta_image == (Image *) NULL)
4106 reconstruct_image=DestroyImage(reconstruct_image);
4107 ThrowMSESimilarityException();
4109 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4110 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4111 reconstruct_image=DestroyImage(reconstruct_image);
4112 if (status == MagickFalse)
4113 ThrowMSESimilarityException();
4117 sum_image=SIMSquareImage(reconstruct,exception);
4118 if (sum_image == (Image *) NULL)
4119 ThrowMSESimilarityException();
4120 channel_statistics=GetImageStatistics(sum_image,exception);
4121 if (channel_statistics == (ChannelStatistics *) NULL)
4122 ThrowMSESimilarityException();
4123 status=SetImageStorageClass(sum_image,DirectClass,exception);
4124 if (status == MagickFalse)
4125 ThrowMSESimilarityException();
4126 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4127 channel_statistics=(ChannelStatistics *)
4128 RelinquishMagickMemory(channel_statistics);
4129 if (status == MagickFalse)
4130 ThrowMSESimilarityException();
4134 AppendImageToList(&sum_image,alpha_image);
4135 AppendImageToList(&sum_image,beta_image);
4136 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4137 if (mean_image == (Image *) NULL)
4138 ThrowMSESimilarityException();
4139 sum_image=DestroyImage(sum_image);
4140 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4141 if (status == MagickFalse)
4142 ThrowMSESimilarityException();
4146 SetGeometry(image,&geometry);
4147 geometry.width=image->columns;
4148 geometry.height=image->rows;
4149 (void) ResetImagePage(mean_image,
"0x0+0+0");
4150 mse_image=CropImage(mean_image,&geometry,exception);
4151 mean_image=DestroyImage(mean_image);
4152 if (mse_image == (Image *) NULL)
4153 ThrowMSESimilarityException();
4157 (void) ResetImagePage(mse_image,
"0x0+0+0");
4158 (void) ClampImage(mse_image,exception);
4159 mse_image->depth=32;
4160 mse_image->colorspace=GRAYColorspace;
4161 mse_image->alpha_trait=UndefinedPixelTrait;
4162 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4163 if (status == MagickFalse)
4164 ThrowMSESimilarityException();
4165 status=NegateImage(mse_image,MagickFalse,exception);
4166 if (status == MagickFalse)
4167 ThrowMSESimilarityException();
4168 alpha_image=DestroyImage(alpha_image);
4169 beta_image=DestroyImage(beta_image);
4170 if ((QuantumScale*minima) < FLT_EPSILON)
4172 *similarity_metric=QuantumScale*minima;
4176static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4177 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4179#define ThrowNCCSimilarityException() \
4181 if (alpha_image != (Image *) NULL) \
4182 alpha_image=DestroyImage(alpha_image); \
4183 if (beta_image != (Image *) NULL) \
4184 beta_image=DestroyImage(beta_image); \
4185 if (channel_statistics != (ChannelStatistics *) NULL) \
4186 channel_statistics=(ChannelStatistics *) \
4187 RelinquishMagickMemory(channel_statistics); \
4188 if (correlation_image != (Image *) NULL) \
4189 correlation_image=DestroyImage(correlation_image); \
4190 if (divide_image != (Image *) NULL) \
4191 divide_image=DestroyImage(divide_image); \
4192 if (ncc_image != (Image *) NULL) \
4193 ncc_image=DestroyImage(ncc_image); \
4194 if (normalize_image != (Image *) NULL) \
4195 normalize_image=DestroyImage(normalize_image); \
4196 if (reconstruct_image != (Image *) NULL) \
4197 reconstruct_image=DestroyImage(reconstruct_image); \
4198 if (target_image != (Image *) NULL) \
4199 target_image=DestroyImage(target_image); \
4200 if (variance_image != (Image *) NULL) \
4201 variance_image=DestroyImage(variance_image); \
4202 return((Image *) NULL); \
4206 *channel_statistics = (ChannelStatistics *) NULL;
4212 *alpha_image = (Image *) NULL,
4213 *beta_image = (Image *) NULL,
4214 *correlation_image = (Image *) NULL,
4215 *divide_image = (Image *) NULL,
4216 *ncc_image = (Image *) NULL,
4217 *normalize_image = (Image *) NULL,
4218 *reconstruct_image = (Image *) NULL,
4219 *target_image = (Image *) NULL,
4220 *variance_image = (Image *) NULL;
4223 status = MagickTrue;
4231 target_image=SIMSquareImage(image,exception);
4232 if (target_image == (Image *) NULL)
4233 ThrowNCCSimilarityException();
4234 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4235 if (reconstruct_image == (Image *) NULL)
4236 ThrowNCCSimilarityException();
4240 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4242 target_image=DestroyImage(target_image);
4243 if (alpha_image == (Image *) NULL)
4244 ThrowNCCSimilarityException();
4245 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4246 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4248 if (status == MagickFalse)
4249 ThrowNCCSimilarityException();
4253 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4254 reconstruct_image=DestroyImage(reconstruct_image);
4255 if (beta_image == (Image *) NULL)
4256 ThrowNCCSimilarityException();
4257 target_image=SIMSquareImage(beta_image,exception);
4258 beta_image=DestroyImage(beta_image);
4259 if (target_image == (Image *) NULL)
4260 ThrowNCCSimilarityException();
4261 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4262 (
const ChannelStatistics *) NULL,exception);
4263 if (status == MagickFalse)
4264 ThrowNCCSimilarityException();
4268 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4269 target_image=DestroyImage(target_image);
4270 alpha_image=DestroyImage(alpha_image);
4271 if (variance_image == (Image *) NULL)
4272 ThrowNCCSimilarityException();
4276 channel_statistics=GetImageStatistics(reconstruct,exception);
4277 if (channel_statistics == (ChannelStatistics *) NULL)
4278 ThrowNCCSimilarityException();
4279 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4280 if (status == MagickFalse)
4281 ThrowNCCSimilarityException();
4282 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4284 channel_statistics=(ChannelStatistics *)
4285 RelinquishMagickMemory(channel_statistics);
4286 if (normalize_image == (Image *) NULL)
4287 ThrowNCCSimilarityException();
4288 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4289 normalize_image=DestroyImage(normalize_image);
4290 if (correlation_image == (Image *) NULL)
4291 ThrowNCCSimilarityException();
4295 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4296 correlation_image=DestroyImage(correlation_image);
4297 variance_image=DestroyImage(variance_image);
4298 if (divide_image == (Image *) NULL)
4299 ThrowNCCSimilarityException();
4303 SetGeometry(image,&geometry);
4304 geometry.width=image->columns;
4305 geometry.height=image->rows;
4306 (void) ResetImagePage(divide_image,
"0x0+0+0");
4307 ncc_image=CropImage(divide_image,&geometry,exception);
4308 divide_image=DestroyImage(divide_image);
4309 if (ncc_image == (Image *) NULL)
4310 ThrowNCCSimilarityException();
4314 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4315 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4316 if (status == MagickFalse)
4317 ThrowNCCSimilarityException();
4318 ncc_image->depth=32;
4319 ncc_image->colorspace=GRAYColorspace;
4320 ncc_image->alpha_trait=UndefinedPixelTrait;
4321 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4322 if (status == MagickFalse)
4323 ThrowNCCSimilarityException();
4324 if ((QuantumScale*maxima) > 1.0)
4326 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4327 (
const ChannelStatistics *) NULL,exception);
4328 maxima=(double) QuantumRange;
4330 *similarity_metric=QuantumScale*maxima;
4334static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4335 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4337#define ThrowPhaseSimilarityException() \
4339 if (correlation_image != (Image *) NULL) \
4340 correlation_image=DestroyImage(correlation_image); \
4341 if (fft_images != (Image *) NULL) \
4342 fft_images=DestroyImageList(fft_images); \
4343 if (gamma_image != (Image *) NULL) \
4344 gamma_image=DestroyImage(gamma_image); \
4345 if (magnitude_image != (Image *) NULL) \
4346 magnitude_image=DestroyImage(magnitude_image); \
4347 if (phase_image != (Image *) NULL) \
4348 phase_image=DestroyImage(phase_image); \
4349 if (reconstruct_image != (Image *) NULL) \
4350 reconstruct_image=DestroyImage(reconstruct_image); \
4351 if (reconstruct_magnitude != (Image *) NULL) \
4352 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4353 if (target_image != (Image *) NULL) \
4354 target_image=DestroyImage(target_image); \
4355 if (test_magnitude != (Image *) NULL) \
4356 test_magnitude=DestroyImage(test_magnitude); \
4357 return((Image *) NULL); \
4364 *correlation_image = (Image *) NULL,
4365 *fft_images = (Image *) NULL,
4366 *gamma_image = (Image *) NULL,
4367 *magnitude_image = (Image *) NULL,
4368 *phase_image = (Image *) NULL,
4369 *reconstruct_image = (Image *) NULL,
4370 *reconstruct_magnitude = (Image *) NULL,
4371 *target_image = (Image *) NULL,
4372 *test_magnitude = (Image *) NULL;
4375 status = MagickTrue;
4383 target_image=CloneImage(image,0,0,MagickTrue,exception);
4384 if (target_image == (Image *) NULL)
4385 ThrowPhaseSimilarityException();
4386 (void) ResetImagePage(target_image,
"0x0+0+0");
4387 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4388 &target_image->background_color);
4389 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4390 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4391 if (status == MagickFalse)
4392 ThrowPhaseSimilarityException();
4396 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4397 if (reconstruct_image == (Image *) NULL)
4398 ThrowPhaseSimilarityException();
4399 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4400 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4401 &reconstruct_image->background_color);
4402 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4403 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4404 if (status == MagickFalse)
4405 ThrowPhaseSimilarityException();
4409 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4410 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4411 if (fft_images == (Image *) NULL)
4412 ThrowPhaseSimilarityException();
4413 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4414 fft_images=DestroyImageList(fft_images);
4415 if (test_magnitude == (Image *) NULL)
4416 ThrowPhaseSimilarityException();
4417 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4418 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4420 if (fft_images == (Image *) NULL)
4421 ThrowPhaseSimilarityException();
4422 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4423 fft_images=DestroyImageList(fft_images);
4424 if (reconstruct_magnitude == (Image *) NULL)
4425 ThrowPhaseSimilarityException();
4426 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4427 if (magnitude_image == (Image *) NULL)
4428 ThrowPhaseSimilarityException();
4429 DisableCompositeClampUnlessSpecified(magnitude_image);
4430 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4431 MagickTrue,0,0,exception);
4435 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4436 magnitude_image,exception);
4437 target_image=DestroyImage(target_image);
4438 reconstruct_image=DestroyImage(reconstruct_image);
4439 test_magnitude=DestroyImage(test_magnitude);
4440 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4441 if (correlation_image == (Image *) NULL)
4442 ThrowPhaseSimilarityException();
4446 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4447 correlation_image=DestroyImage(correlation_image);
4448 if (gamma_image == (Image *) NULL)
4449 ThrowPhaseSimilarityException();
4453 SetGeometry(image,&geometry);
4454 geometry.width=image->columns;
4455 geometry.height=image->rows;
4456 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4457 phase_image=CropImage(gamma_image,&geometry,exception);
4458 gamma_image=DestroyImage(gamma_image);
4459 if (phase_image == (Image *) NULL)
4460 ThrowPhaseSimilarityException();
4461 (void) ResetImagePage(phase_image,
"0x0+0+0");
4465 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4466 if (status == MagickFalse)
4467 ThrowPhaseSimilarityException();
4468 phase_image->depth=32;
4469 phase_image->colorspace=GRAYColorspace;
4470 phase_image->alpha_trait=UndefinedPixelTrait;
4471 status=SIMFilterImageNaNs(phase_image,exception);
4472 if (status == MagickFalse)
4473 ThrowPhaseSimilarityException();
4474 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4475 if (status == MagickFalse)
4476 ThrowPhaseSimilarityException();
4477 magnitude_image=DestroyImage(magnitude_image);
4478 if ((QuantumScale*maxima) > 1.0)
4480 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4481 (
const ChannelStatistics *) NULL,exception);
4482 maxima=(double) QuantumRange;
4484 *similarity_metric=QuantumScale*maxima;
4485 return(phase_image);
4488static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4489 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4492 *psnr_image = (Image *) NULL;
4494 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4496 if (psnr_image == (Image *) NULL)
4498 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4499 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4503static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4504 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4507 *rmse_image = (Image *) NULL;
4509 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4511 if (rmse_image == (Image *) NULL)
4513 *similarity_metric=sqrt(*similarity_metric);
4518static double GetSimilarityMetric(
const Image *image,
4519 const Image *reconstruct_image,
const MetricType metric,
4520 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4523 *channel_similarity,
4527 *sans_exception = AcquireExceptionInfo();
4533 status = MagickTrue;
4539 length = MaxPixelChannels+1UL;
4541 SetGeometry(reconstruct_image,&geometry);
4542 geometry.x=x_offset;
4543 geometry.y=y_offset;
4544 similarity_image=CropImage(image,&geometry,sans_exception);
4545 sans_exception=DestroyExceptionInfo(sans_exception);
4546 if (similarity_image == (Image *) NULL)
4551 channel_similarity=(
double *) AcquireQuantumMemory(length,
4552 sizeof(*channel_similarity));
4553 if (channel_similarity == (
double *) NULL)
4554 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4555 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4558 case AbsoluteErrorMetric:
4560 status=GetAESimilarity(similarity_image,reconstruct_image,
4561 channel_similarity,exception);
4564 case DotProductCorrelationErrorMetric:
4566 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4567 channel_similarity,exception);
4570 case FuzzErrorMetric:
4572 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4573 channel_similarity,exception);
4576 case MeanAbsoluteErrorMetric:
4578 status=GetMAESimilarity(similarity_image,reconstruct_image,
4579 channel_similarity,exception);
4582 case MeanErrorPerPixelErrorMetric:
4584 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4585 channel_similarity,exception);
4588 case MeanSquaredErrorMetric:
4590 status=GetMSESimilarity(similarity_image,reconstruct_image,
4591 channel_similarity,exception);
4594 case NormalizedCrossCorrelationErrorMetric:
4596 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4597 channel_similarity,exception);
4600 case PeakAbsoluteErrorMetric:
4602 status=GetPASimilarity(similarity_image,reconstruct_image,
4603 channel_similarity,exception);
4606 case PeakSignalToNoiseRatioErrorMetric:
4608 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4609 channel_similarity,exception);
4612 case PerceptualHashErrorMetric:
4614 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4615 channel_similarity,exception);
4618 case PhaseCorrelationErrorMetric:
4620 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4621 channel_similarity,exception);
4624 case PixelDifferenceCountErrorMetric:
4626 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4627 channel_similarity,exception);
4630 case RootMeanSquaredErrorMetric:
4631 case UndefinedErrorMetric:
4634 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4635 channel_similarity,exception);
4638 case StructuralDissimilarityErrorMetric:
4640 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4641 channel_similarity,exception);
4644 case StructuralSimilarityErrorMetric:
4646 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4647 channel_similarity,exception);
4651 similarity_image=DestroyImage(similarity_image);
4652 similarity=channel_similarity[CompositePixelChannel];
4653 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4654 if (status == MagickFalse)
4659MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4660 const MetricType metric,
const double similarity_threshold,
4661 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4663#define SimilarityImageTag "Similarity/Image"
4679 *similarity_image = (Image *) NULL;
4682 status = MagickTrue;
4688 similarity_info = { 0.0, 0, 0 };
4697 assert(image != (
const Image *) NULL);
4698 assert(image->signature == MagickCoreSignature);
4699 assert(exception != (ExceptionInfo *) NULL);
4700 assert(exception->signature == MagickCoreSignature);
4701 assert(offset != (RectangleInfo *) NULL);
4702 if (IsEventLogging() != MagickFalse)
4703 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4704 SetGeometry(reconstruct,offset);
4705 *similarity_metric=0.0;
4708#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4710 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4711 if (artifact == (
const char *) NULL)
4712 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4713 if (((artifact == (
const char *) NULL) ||
4714 (IsStringTrue(artifact) != MagickFalse)) &&
4715 ((image->channels & ReadMaskChannel) == 0))
4718 case DotProductCorrelationErrorMetric:
4720 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4721 similarity_metric,exception);
4722 return(similarity_image);
4724 case MeanSquaredErrorMetric:
4726 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4727 similarity_metric,exception);
4728 return(similarity_image);
4730 case NormalizedCrossCorrelationErrorMetric:
4732 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4733 similarity_metric,exception);
4734 return(similarity_image);
4736 case PeakSignalToNoiseRatioErrorMetric:
4738 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4739 similarity_metric,exception);
4740 return(similarity_image);
4742 case PhaseCorrelationErrorMetric:
4744 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4745 similarity_metric,exception);
4746 return(similarity_image);
4748 case RootMeanSquaredErrorMetric:
4749 case UndefinedErrorMetric:
4751 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4752 similarity_metric,exception);
4753 return(similarity_image);
4760 if ((image->columns < reconstruct->columns) ||
4761 (image->rows < reconstruct->rows))
4763 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4764 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4765 return((Image *) NULL);
4767 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4768 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4769 if (similarity_image == (Image *) NULL)
4770 return((Image *) NULL);
4771 similarity_image->depth=32;
4772 similarity_image->colorspace=GRAYColorspace;
4773 similarity_image->alpha_trait=UndefinedPixelTrait;
4774 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4775 if (status == MagickFalse)
4776 return(DestroyImage(similarity_image));
4780 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4781 similarity_info.x,similarity_info.y,exception);
4782 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4783#if defined(MAGICKCORE_OPENMP_SUPPORT)
4784 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4785 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4787 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4793 threshold_trigger = MagickFalse;
4799 channel_info = similarity_info;
4804 if (status == MagickFalse)
4806 if (threshold_trigger != MagickFalse)
4808 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4809 similarity_image->columns,1,exception);
4810 if (q == (Quantum *) NULL)
4815 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4820 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4824 case DotProductCorrelationErrorMetric:
4825 case NormalizedCrossCorrelationErrorMetric:
4826 case PeakSignalToNoiseRatioErrorMetric:
4827 case PhaseCorrelationErrorMetric:
4828 case StructuralSimilarityErrorMetric:
4830 if (similarity <= channel_info.similarity)
4832 channel_info.similarity=similarity;
4839 if (similarity >= channel_info.similarity)
4841 channel_info.similarity=similarity;
4847 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4849 PixelChannel channel = GetPixelChannelChannel(image,i);
4850 PixelTrait traits = GetPixelChannelTraits(image,channel);
4851 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4853 if (((traits & UpdatePixelTrait) == 0) ||
4854 ((similarity_traits & UpdatePixelTrait) == 0))
4858 case DotProductCorrelationErrorMetric:
4859 case NormalizedCrossCorrelationErrorMetric:
4860 case PeakSignalToNoiseRatioErrorMetric:
4861 case PhaseCorrelationErrorMetric:
4862 case StructuralSimilarityErrorMetric:
4864 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4865 QuantumRange*similarity),q);
4870 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4871 QuantumRange*(1.0-similarity)),q);
4876 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4878#if defined(MAGICKCORE_OPENMP_SUPPORT)
4879 #pragma omp critical (MagickCore_GetSimilarityMetric)
4883 case DotProductCorrelationErrorMetric:
4884 case NormalizedCrossCorrelationErrorMetric:
4885 case PeakSignalToNoiseRatioErrorMetric:
4886 case PhaseCorrelationErrorMetric:
4887 case StructuralSimilarityErrorMetric:
4889 if (similarity_threshold != DefaultSimilarityThreshold)
4890 if (channel_info.similarity >= similarity_threshold)
4891 threshold_trigger=MagickTrue;
4892 if (channel_info.similarity >= similarity_info.similarity)
4893 similarity_info=channel_info;
4898 if (similarity_threshold != DefaultSimilarityThreshold)
4899 if (channel_info.similarity < similarity_threshold)
4900 threshold_trigger=MagickTrue;
4901 if (channel_info.similarity < similarity_info.similarity)
4902 similarity_info=channel_info;
4906 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4908 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4914 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4915 if (proceed == MagickFalse)
4919 similarity_view=DestroyCacheView(similarity_view);
4920 if (status == MagickFalse)
4921 similarity_image=DestroyImage(similarity_image);
4922 *similarity_metric=similarity_info.similarity;
4923 if (fabs(*similarity_metric) < MagickEpsilon)
4924 *similarity_metric=0.0;
4925 offset->x=similarity_info.x;
4926 offset->y=similarity_info.y;
4927 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4928 GetMagickPrecision(),*similarity_metric);
4929 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4930 GetMagickPrecision(),(
double) offset->x);
4931 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4932 GetMagickPrecision(),(
double) offset->y);
4933 return(similarity_image);