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)
1534 status = MagickTrue;
1546 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1547 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1548 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1549 image_view=AcquireVirtualCacheView(image,exception);
1550 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1551#if defined(MAGICKCORE_OPENMP_SUPPORT)
1552 #pragma omp parallel for schedule(static) shared(similarity,status) \
1553 magick_number_threads(image,image,rows,1)
1555 for (y=0; y < (ssize_t) rows; y++)
1562 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1567 if (status == MagickFalse)
1569 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1570 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1571 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1576 for (x=0; x < (ssize_t) columns; x++)
1588 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1589 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1591 p+=(ptrdiff_t) GetPixelChannels(image);
1592 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1595 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1596 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1597 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1602 PixelChannel channel = GetPixelChannelChannel(image,i);
1603 PixelTrait traits = GetPixelChannelTraits(image,channel);
1604 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1606 if (((traits & UpdatePixelTrait) == 0) ||
1607 ((reconstruct_traits & UpdatePixelTrait) == 0))
1609 if (channel == AlphaPixelChannel)
1610 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1613 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1614 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1616 channel_similarity[i]++;
1621 channel_similarity[CompositePixelChannel]++;
1622 p+=(ptrdiff_t) GetPixelChannels(image);
1623 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1625#if defined(MAGICKCORE_OPENMP_SUPPORT)
1626 #pragma omp critical (MagickCore_GetPDCSimilarity)
1632 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1634 PixelChannel channel = GetPixelChannelChannel(image,j);
1635 PixelTrait traits = GetPixelChannelTraits(image,channel);
1636 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1638 if (((traits & UpdatePixelTrait) == 0) ||
1639 ((reconstruct_traits & UpdatePixelTrait) == 0))
1641 similarity[j]+=channel_similarity[j];
1643 similarity[CompositePixelChannel]+=
1644 channel_similarity[CompositePixelChannel];
1647 reconstruct_view=DestroyCacheView(reconstruct_view);
1648 image_view=DestroyCacheView(image_view);
1652static Image *GetPHASECorrelationSurface(
const Image *image,
1653 ExceptionInfo *exception)
1665 kernel=AcquireKernelInfo(
"3x3: 0,-1,0 -1,4,-1 0,-1,0",exception);
1666 if (kernel == (KernelInfo *) NULL)
1667 return((Image *) NULL);
1668 surface=MorphologyImage(image,ConvolveMorphology,1,kernel,exception);
1669 kernel=DestroyKernelInfo(kernel);
1673static MagickBooleanType GetPHASESimilarity(
const Image *image,
1674 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1681 correlation[MaxPixelChannels+1] = { 0.0 },
1683 image_sum[MaxPixelChannels+1] = { 0.0 },
1684 image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1685 reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1686 reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1693 status = MagickTrue;
1708 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1709 phase_image=GetPHASECorrelationSurface(image,exception);
1710 phase_reconstruct=GetPHASECorrelationSurface(reconstruct_image,exception);
1711 if ((phase_image == (Image *) NULL) ||
1712 (phase_reconstruct == (Image *) NULL))
1714 if (phase_image != (Image *) NULL)
1715 phase_image=DestroyImage(phase_image);
1716 if (phase_reconstruct != (Image *) NULL)
1717 phase_reconstruct=DestroyImage(phase_reconstruct);
1718 return(MagickFalse);
1720 image_view=AcquireVirtualCacheView(phase_image,exception);
1721 reconstruct_view=AcquireVirtualCacheView(phase_reconstruct,exception);
1722#if defined(MAGICKCORE_OPENMP_SUPPORT)
1723 #pragma omp parallel for schedule(static) shared(status) \
1724 magick_number_threads(phase_image,phase_reconstruct,rows,1)
1726 for (y=0; y < (ssize_t) rows; y++)
1733 channel_correlation[MaxPixelChannels+1] = { 0.0 },
1735 channel_image_sum[MaxPixelChannels+1] = { 0.0 },
1736 channel_image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1737 channel_reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1738 channel_reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1744 if (status == MagickFalse)
1746 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1747 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1748 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1753 for (x=0; x < (ssize_t) columns; x++)
1755 if ((GetPixelReadMask(phase_image,p) <= (QuantumRange/2)) ||
1756 (GetPixelReadMask(phase_reconstruct,q) <= (QuantumRange/2)))
1758 p+=(ptrdiff_t) GetPixelChannels(phase_image);
1759 q+=(ptrdiff_t) GetPixelChannels(phase_reconstruct);
1762 for (i=0; i < (ssize_t) GetPixelChannels(phase_image); i++)
1778 channel=GetPixelChannelChannel(phase_image,i);
1779 traits=GetPixelChannelTraits(phase_image,channel);
1780 reconstruct_traits=GetPixelChannelTraits(phase_reconstruct,channel);
1781 if (((traits & UpdatePixelTrait) == 0) ||
1782 ((reconstruct_traits & UpdatePixelTrait) == 0))
1784 offset=GetPixelChannelOffset(phase_reconstruct,channel);
1787 alpha=QuantumScale*(double) p[i];
1788 beta=QuantumScale*(double) q[offset];
1789 channel_image_sum[i]+=alpha;
1790 channel_image_sum_squared[i]+=alpha*alpha;
1791 channel_reconstruct_sum[i]+=beta;
1792 channel_reconstruct_sum_squared[i]+=beta*beta;
1793 channel_correlation[i]+=alpha*beta;
1796 p+=(ptrdiff_t) GetPixelChannels(phase_image);
1797 q+=(ptrdiff_t) GetPixelChannels(phase_reconstruct);
1799#if defined(MAGICKCORE_OPENMP_SUPPORT)
1800 #pragma omp critical (MagickCore_GetPHASESimilarity)
1803 count+=channel_count;
1804 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1806 correlation[i]+=channel_correlation[i];
1807 image_sum[i]+=channel_image_sum[i];
1808 image_sum_squared[i]+=channel_image_sum_squared[i];
1809 reconstruct_sum[i]+=channel_reconstruct_sum[i];
1810 reconstruct_sum_squared[i]+=channel_reconstruct_sum_squared[i];
1814 reconstruct_view=DestroyCacheView(reconstruct_view);
1815 image_view=DestroyCacheView(image_view);
1816 phase_image=DestroyImage(phase_image);
1817 phase_reconstruct=DestroyImage(phase_reconstruct);
1818 if ((status == MagickFalse) || (count < 1.0))
1824 similarity[CompositePixelChannel]=0.0;
1825 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1839 channel=GetPixelChannelChannel(image,j);
1840 traits=GetPixelChannelTraits(image,channel);
1841 reconstruct_traits=GetPixelChannelTraits(reconstruct_image,channel);
1842 if (((traits & UpdatePixelTrait) == 0) ||
1843 ((reconstruct_traits & UpdatePixelTrait) == 0))
1845 numerator=count*correlation[j]-image_sum[j]*reconstruct_sum[j];
1846 denominator=sqrt(count*image_sum_squared[j]-image_sum[j]*image_sum[j])*
1847 sqrt(count*reconstruct_sum_squared[j]-reconstruct_sum[j]*
1848 reconstruct_sum[j]);
1849 pearson=denominator < MagickEpsilon ? 0.0 : numerator/denominator;
1854 similarity[j]=pearson;
1855 similarity[CompositePixelChannel]+=pearson;
1859 similarity[CompositePixelChannel]/=(double) channels;
1863static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1864 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1866 ChannelPerceptualHash
1879 channel_phash=GetImagePerceptualHash(image,exception);
1880 if (channel_phash == (ChannelPerceptualHash *) NULL)
1881 return(MagickFalse);
1882 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1883 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1885 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1887 return(MagickFalse);
1889 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1897 PixelChannel channel = GetPixelChannelChannel(image,i);
1898 PixelTrait traits = GetPixelChannelTraits(image,channel);
1899 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1901 if (((traits & UpdatePixelTrait) == 0) ||
1902 ((reconstruct_traits & UpdatePixelTrait) == 0))
1904 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1913 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1918 alpha=channel_phash[i].phash[j][k];
1919 beta=reconstruct_phash[i].phash[j][k];
1921 if (IsNaN(error) != 0)
1923 difference+=error*error;
1926 similarity[i]+=difference;
1927 similarity[CompositePixelChannel]+=difference;
1929 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1930 artifact=GetImageArtifact(image,
"phash:normalize");
1931 if (IsStringTrue(artifact) != MagickFalse)
1933 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1935 PixelChannel channel = GetPixelChannelChannel(image,i);
1936 PixelTrait traits = GetPixelChannelTraits(image,channel);
1937 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1939 if (((traits & UpdatePixelTrait) == 0) ||
1940 ((reconstruct_traits & UpdatePixelTrait) == 0))
1942 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
1944 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
1945 channel_phash[0].number_colorspaces);
1950 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1952 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
1956static MagickBooleanType GetPSNRSimilarity(
const Image *image,
1957 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1960 status = MagickTrue;
1968 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
1969 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1971 PixelChannel channel = GetPixelChannelChannel(image,i);
1972 PixelTrait traits = GetPixelChannelTraits(image,channel);
1973 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1975 if (((traits & UpdatePixelTrait) == 0) ||
1976 ((reconstruct_traits & UpdatePixelTrait) == 0))
1978 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
1979 similarity[i]))/MagickSafePSNRRecipicol(10.0);
1981 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
1982 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
1983 MagickSafePSNRRecipicol(10.0);
1987static MagickBooleanType GetRMSESimilarity(
const Image *image,
1988 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1990#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
1993 status = MagickTrue;
2001 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2002 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2004 PixelChannel channel = GetPixelChannelChannel(image,i);
2005 PixelTrait traits = GetPixelChannelTraits(image,channel);
2006 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2008 if (((traits & UpdatePixelTrait) == 0) ||
2009 ((reconstruct_traits & UpdatePixelTrait) == 0))
2011 similarity[i]=RMSESquareRoot(similarity[i]);
2013 similarity[CompositePixelChannel]=RMSESquareRoot(
2014 similarity[CompositePixelChannel]);
2018static MagickBooleanType GetSSIMSimularity(
const Image *image,
2019 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2021#define SSIMRadius 5.0
2022#define SSIMSigma 1.5
2032 geometry[MagickPathExtent];
2048 status = MagickTrue;
2062 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2063 if (artifact != (
const char *) NULL)
2064 radius=StringToDouble(artifact,(
char **) NULL);
2066 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2067 if (artifact != (
const char *) NULL)
2068 sigma=StringToDouble(artifact,(
char **) NULL);
2069 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.17gx%.17g",
2071 kernel_info=AcquireKernelInfo(geometry,exception);
2072 if (kernel_info == (KernelInfo *) NULL)
2073 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2075 c1=pow(SSIMK1*SSIML,2.0);
2076 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2077 if (artifact != (
const char *) NULL)
2078 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2079 c2=pow(SSIMK2*SSIML,2.0);
2080 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2081 if (artifact != (
const char *) NULL)
2082 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2083 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2084 image_view=AcquireVirtualCacheView(image,exception);
2085 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2086#if defined(MAGICKCORE_OPENMP_SUPPORT)
2087 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2088 magick_number_threads(image,reconstruct_image,rows,1)
2090 for (y=0; y < (ssize_t) rows; y++)
2098 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2104 if (status == MagickFalse)
2106 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2107 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2108 kernel_info->height,exception);
2109 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2110 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2111 kernel_info->height,exception);
2112 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2117 for (x=0; x < (ssize_t) columns; x++)
2120 *magick_restrict reconstruct,
2121 *magick_restrict test;
2124 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2125 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2126 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2127 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2128 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2136 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2137 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2139 p+=(ptrdiff_t) GetPixelChannels(image);
2140 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2143 k=kernel_info->values;
2146 for (v=0; v < (ssize_t) kernel_info->height; v++)
2151 for (u=0; u < (ssize_t) kernel_info->width; u++)
2153 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2159 PixelChannel channel = GetPixelChannelChannel(image,i);
2160 PixelTrait traits = GetPixelChannelTraits(image,channel);
2161 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2162 reconstruct_image,channel);
2163 if (((traits & UpdatePixelTrait) == 0) ||
2164 ((reconstruct_traits & UpdatePixelTrait) == 0))
2166 x_pixel=QuantumScale*(double) test[i];
2167 x_pixel_mu[i]+=(*k)*x_pixel;
2168 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2169 y_pixel=QuantumScale*(double)
2170 GetPixelChannel(reconstruct_image,channel,reconstruct);
2171 y_pixel_mu[i]+=(*k)*y_pixel;
2172 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2173 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2176 test+=(ptrdiff_t) GetPixelChannels(image);
2177 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2179 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2180 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2182 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2187 x_pixel_sigmas_squared,
2191 y_pixel_sigmas_squared;
2193 PixelChannel channel = GetPixelChannelChannel(image,i);
2194 PixelTrait traits = GetPixelChannelTraits(image,channel);
2195 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2196 reconstruct_image,channel);
2197 if (((traits & UpdatePixelTrait) == 0) ||
2198 ((reconstruct_traits & UpdatePixelTrait) == 0))
2200 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2201 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2202 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2203 xy_sigmas=xy_sigma[i]-xy_mu;
2204 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2205 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2206 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2207 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2208 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2209 channel_similarity[i]+=ssim;
2210 channel_similarity[CompositePixelChannel]+=ssim;
2212 p+=(ptrdiff_t) GetPixelChannels(image);
2213 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2216#if defined(MAGICKCORE_OPENMP_SUPPORT)
2217 #pragma omp critical (MagickCore_GetSSIMSimularity)
2224 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2226 PixelChannel channel = GetPixelChannelChannel(image,j);
2227 PixelTrait traits = GetPixelChannelTraits(image,channel);
2228 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2230 if (((traits & UpdatePixelTrait) == 0) ||
2231 ((reconstruct_traits & UpdatePixelTrait) == 0))
2233 similarity[j]+=channel_similarity[j];
2235 similarity[CompositePixelChannel]+=
2236 channel_similarity[CompositePixelChannel];
2239 image_view=DestroyCacheView(image_view);
2240 reconstruct_view=DestroyCacheView(reconstruct_view);
2241 area=MagickSafeReciprocal(area);
2242 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2244 PixelChannel channel = GetPixelChannelChannel(image,l);
2245 PixelTrait traits = GetPixelChannelTraits(image,channel);
2246 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2248 if (((traits & UpdatePixelTrait) == 0) ||
2249 ((reconstruct_traits & UpdatePixelTrait) == 0))
2251 similarity[l]*=area;
2253 similarity[CompositePixelChannel]*=area;
2254 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2255 kernel_info=DestroyKernelInfo(kernel_info);
2259static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2260 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2263 status = MagickTrue;
2271 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2272 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2274 PixelChannel channel = GetPixelChannelChannel(image,i);
2275 PixelTrait traits = GetPixelChannelTraits(image,channel);
2276 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2278 if (((traits & UpdatePixelTrait) == 0) ||
2279 ((reconstruct_traits & UpdatePixelTrait) == 0))
2281 similarity[i]=(1.0-similarity[i])/2.0;
2283 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2287MagickExport MagickBooleanType GetImageDistortion(Image *image,
2288 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2289 ExceptionInfo *exception)
2291#define CompareMetricNotSupportedException "metric not supported"
2294 *channel_similarity;
2297 status = MagickTrue;
2302 assert(image != (Image *) NULL);
2303 assert(image->signature == MagickCoreSignature);
2304 assert(reconstruct_image != (
const Image *) NULL);
2305 assert(reconstruct_image->signature == MagickCoreSignature);
2306 assert(distortion != (
double *) NULL);
2307 if (IsEventLogging() != MagickFalse)
2308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2313 length=MaxPixelChannels+1UL;
2314 channel_similarity=(
double *) AcquireQuantumMemory(length,
2315 sizeof(*channel_similarity));
2316 if (channel_similarity == (
double *) NULL)
2317 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2318 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2321 case AbsoluteErrorMetric:
2323 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2327 case DotProductCorrelationErrorMetric:
2329 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2333 case FuzzErrorMetric:
2335 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2339 case MeanAbsoluteErrorMetric:
2341 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2345 case MeanErrorPerPixelErrorMetric:
2347 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2351 case MeanSquaredErrorMetric:
2353 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2357 case NormalizedCrossCorrelationErrorMetric:
2359 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2363 case PeakAbsoluteErrorMetric:
2365 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2369 case PeakSignalToNoiseRatioErrorMetric:
2371 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2375 case PerceptualHashErrorMetric:
2377 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2381 case PhaseCorrelationErrorMetric:
2383 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2387 case PixelDifferenceCountErrorMetric:
2389 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2393 case RootMeanSquaredErrorMetric:
2394 case UndefinedErrorMetric:
2397 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2401 case StructuralDissimilarityErrorMetric:
2403 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2407 case StructuralSimilarityErrorMetric:
2409 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2414 *distortion=channel_similarity[CompositePixelChannel];
2417 case DotProductCorrelationErrorMetric:
2418 case NormalizedCrossCorrelationErrorMetric:
2419 case PhaseCorrelationErrorMetric:
2420 case StructuralSimilarityErrorMetric:
2422 *distortion=(1.0-(*distortion))/2.0;
2427 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2428 if (fabs(*distortion) < MagickEpsilon)
2430 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2466MagickExport
double *GetImageDistortions(Image *image,
2467 const Image *reconstruct_image,
const MetricType metric,
2468 ExceptionInfo *exception)
2472 *channel_similarity;
2475 status = MagickTrue;
2483 assert(image != (Image *) NULL);
2484 assert(image->signature == MagickCoreSignature);
2485 assert(reconstruct_image != (
const Image *) NULL);
2486 assert(reconstruct_image->signature == MagickCoreSignature);
2487 if (IsEventLogging() != MagickFalse)
2488 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2492 length=MaxPixelChannels+1UL;
2493 channel_similarity=(
double *) AcquireQuantumMemory(length,
2494 sizeof(*channel_similarity));
2495 if (channel_similarity == (
double *) NULL)
2496 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2497 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2500 case AbsoluteErrorMetric:
2502 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2506 case DotProductCorrelationErrorMetric:
2508 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2512 case FuzzErrorMetric:
2514 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2518 case MeanAbsoluteErrorMetric:
2520 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2524 case MeanErrorPerPixelErrorMetric:
2526 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2530 case MeanSquaredErrorMetric:
2532 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2536 case NormalizedCrossCorrelationErrorMetric:
2538 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2542 case PeakAbsoluteErrorMetric:
2544 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2548 case PeakSignalToNoiseRatioErrorMetric:
2550 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2554 case PerceptualHashErrorMetric:
2556 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2560 case PhaseCorrelationErrorMetric:
2562 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2566 case PixelDifferenceCountErrorMetric:
2568 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2572 case RootMeanSquaredErrorMetric:
2573 case UndefinedErrorMetric:
2576 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2580 case StructuralDissimilarityErrorMetric:
2582 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2586 case StructuralSimilarityErrorMetric:
2588 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2593 if (status == MagickFalse)
2595 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2596 return((
double *) NULL);
2598 distortion=channel_similarity;
2601 case DotProductCorrelationErrorMetric:
2602 case NormalizedCrossCorrelationErrorMetric:
2603 case PhaseCorrelationErrorMetric:
2604 case StructuralSimilarityErrorMetric:
2606 for (i=0; i <= MaxPixelChannels; i++)
2607 distortion[i]=(1.0-distortion[i])/2.0;
2612 for (i=0; i <= MaxPixelChannels; i++)
2613 if (fabs(distortion[i]) < MagickEpsilon)
2615 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2616 distortion[CompositePixelChannel]);
2648MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2649 const Image *reconstruct_image,ExceptionInfo *exception)
2662 assert(image != (Image *) NULL);
2663 assert(image->signature == MagickCoreSignature);
2664 assert(reconstruct_image != (
const Image *) NULL);
2665 assert(reconstruct_image->signature == MagickCoreSignature);
2666 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2667 image_view=AcquireVirtualCacheView(image,exception);
2668 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2669 for (y=0; y < (ssize_t) rows; y++)
2678 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2679 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2680 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2682 for (x=0; x < (ssize_t) columns; x++)
2687 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2692 PixelChannel channel = GetPixelChannelChannel(image,i);
2693 PixelTrait traits = GetPixelChannelTraits(image,channel);
2694 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2696 if (((traits & UpdatePixelTrait) == 0) ||
2697 ((reconstruct_traits & UpdatePixelTrait) == 0))
2699 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2701 if (distance >= MagickEpsilon)
2704 if (i < (ssize_t) GetPixelChannels(image))
2706 p+=(ptrdiff_t) GetPixelChannels(image);
2707 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2709 if (x < (ssize_t) columns)
2712 reconstruct_view=DestroyCacheView(reconstruct_view);
2713 image_view=DestroyCacheView(image_view);
2714 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2766MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2767 const Image *reconstruct_image,ExceptionInfo *exception)
2770 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2775 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2777 if (status == MagickFalse)
2778 return(MagickFalse);
2779 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2780 MagickTrue : MagickFalse;
2827#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2828static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2829 const Image *beta_image,ExceptionInfo *exception)
2832 *alpha_fft = (Image *) NULL,
2833 *beta_fft = (Image *) NULL,
2834 *complex_conjugate = (Image *) NULL,
2835 *complex_multiplication = (Image *) NULL,
2836 *cross_correlation = (Image *) NULL,
2837 *temp_image = (Image *) NULL;
2842 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2843 if (temp_image == (Image *) NULL)
2844 return((Image *) NULL);
2845 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2846 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2847 temp_image=DestroyImageList(temp_image);
2848 if (beta_fft == (Image *) NULL)
2849 return((Image *) NULL);
2853 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2854 beta_fft=DestroyImageList(beta_fft);
2855 if (complex_conjugate == (Image *) NULL)
2856 return((Image *) NULL);
2860 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2861 if (temp_image == (Image *) NULL)
2863 complex_conjugate=DestroyImageList(complex_conjugate);
2864 return((Image *) NULL);
2866 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2867 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2868 temp_image=DestroyImageList(temp_image);
2869 if (alpha_fft == (Image *) NULL)
2871 complex_conjugate=DestroyImageList(complex_conjugate);
2872 return((Image *) NULL);
2877 DisableCompositeClampUnlessSpecified(complex_conjugate);
2878 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2879 AppendImageToList(&complex_conjugate,alpha_fft);
2880 complex_multiplication=ComplexImages(complex_conjugate,
2881 MultiplyComplexOperator,exception);
2882 complex_conjugate=DestroyImageList(complex_conjugate);
2883 if (complex_multiplication == (Image *) NULL)
2884 return((Image *) NULL);
2888 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2889 complex_multiplication->next,MagickFalse,exception);
2890 complex_multiplication=DestroyImageList(complex_multiplication);
2891 return(cross_correlation);
2894static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2895 ExceptionInfo *exception)
2903 kernel_info=AcquireKernelInfo(kernel,exception);
2904 if (kernel_info == (KernelInfo *) NULL)
2905 return((Image *) NULL);
2906 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2908 kernel_info=DestroyKernelInfo(kernel_info);
2909 return(derivative_image);
2912static Image *SIMDivideImage(
const Image *numerator_image,
2913 const Image *denominator_image,ExceptionInfo *exception)
2923 status = MagickTrue;
2931 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2932 if (divide_image == (Image *) NULL)
2933 return(divide_image);
2934 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2935 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2936#if defined(MAGICKCORE_OPENMP_SUPPORT)
2937 #pragma omp parallel for schedule(static) shared(status) \
2938 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
2940 for (y=0; y < (ssize_t) divide_image->rows; y++)
2951 if (status == MagickFalse)
2953 p=GetCacheViewVirtualPixels(denominator_view,0,y,
2954 denominator_image->columns,1,exception);
2955 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
2957 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2962 for (x=0; x < (ssize_t) divide_image->columns; x++)
2967 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
2969 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
2970 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
2971 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
2973 if (((traits & UpdatePixelTrait) == 0) ||
2974 ((denominator_traits & UpdatePixelTrait) == 0))
2976 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
2977 (
double) GetPixelChannel(denominator_image,channel,p)));
2979 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
2980 q+=(ptrdiff_t) GetPixelChannels(divide_image);
2982 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
2985 denominator_view=DestroyCacheView(denominator_view);
2986 numerator_view=DestroyCacheView(numerator_view);
2987 if (status == MagickFalse)
2988 divide_image=DestroyImage(divide_image);
2989 return(divide_image);
2992static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
2993 const Image *source_image,ExceptionInfo *exception)
3002 divide_image=SIMDivideImage(image,magnitude_image,exception);
3003 if (divide_image == (Image *) NULL)
3004 return((Image *) NULL);
3005 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3006 ÷_image->background_color);
3007 SetGeometry(source_image,&geometry);
3008 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3009 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3010 result_image=ExtentImage(divide_image,&geometry,exception);
3011 divide_image=DestroyImage(divide_image);
3012 return(result_image);
3015static MagickBooleanType SIMFilterImageNaNs(Image *image,
3016 ExceptionInfo *exception)
3022 status = MagickTrue;
3030 image_view=AcquireAuthenticCacheView(image,exception);
3031#if defined(MAGICKCORE_OPENMP_SUPPORT)
3032 #pragma omp parallel for schedule(static) shared(status) \
3033 magick_number_threads(image,image,image->rows,1)
3035 for (y=0; y < (ssize_t) image->rows; y++)
3043 if (status == MagickFalse)
3045 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3046 if (q == (Quantum *) NULL)
3051 for (x=0; x < (ssize_t) image->columns; x++)
3056 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3058 PixelChannel channel = GetPixelChannelChannel(image,i);
3059 PixelTrait traits = GetPixelChannelTraits(image,channel);
3060 if ((traits & UpdatePixelTrait) == 0)
3062 if (IsNaN((
double) q[i]) != 0)
3065 q+=(ptrdiff_t) GetPixelChannels(image);
3067 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3070 image_view=DestroyCacheView(image_view);
3074static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3083 status = MagickTrue;
3091 square_image=CloneImage(image,0,0,MagickTrue,exception);
3092 if (square_image == (Image *) NULL)
3093 return(square_image);
3094 image_view=AcquireAuthenticCacheView(square_image,exception);
3095#if defined(MAGICKCORE_OPENMP_SUPPORT)
3096 #pragma omp parallel for schedule(static) shared(status) \
3097 magick_number_threads(square_image,square_image,square_image->rows,1)
3099 for (y=0; y < (ssize_t) square_image->rows; y++)
3107 if (status == MagickFalse)
3109 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3111 if (q == (Quantum *) NULL)
3116 for (x=0; x < (ssize_t) square_image->columns; x++)
3121 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3123 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3124 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3125 if ((traits & UpdatePixelTrait) == 0)
3127 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3129 q+=(ptrdiff_t) GetPixelChannels(square_image);
3131 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3134 image_view=DestroyCacheView(image_view);
3135 if (status == MagickFalse)
3136 square_image=DestroyImage(square_image);
3137 return(square_image);
3140static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3141 ExceptionInfo *exception)
3149 status = MagickTrue;
3151 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3152 xsq_image=SIMSquareImage(alpha_image,exception);
3153 if (xsq_image == (Image *) NULL)
3154 return((Image *) NULL);
3155 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3156 ysq_image=SIMSquareImage(beta_image,exception);
3157 if (ysq_image == (Image *) NULL)
3159 xsq_image=DestroyImage(xsq_image);
3160 return((Image *) NULL);
3162 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3164 magnitude_image=xsq_image;
3165 ysq_image=DestroyImage(ysq_image);
3166 if (status == MagickFalse)
3168 magnitude_image=DestroyImage(magnitude_image);
3169 return((Image *) NULL);
3171 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3172 if (status == MagickFalse)
3174 magnitude_image=DestroyImage(magnitude_image);
3175 return (Image *) NULL;
3177 return(magnitude_image);
3180static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3181 RectangleInfo *offset,ExceptionInfo *exception)
3200 status = MagickTrue;
3203 maxima_info = { -MagickMaximumValue, 0, 0 };
3211 image_view=AcquireVirtualCacheView(image,exception);
3212 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3214 if (q != (
const Quantum *) NULL)
3215 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3216#if defined(MAGICKCORE_OPENMP_SUPPORT)
3217 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3218 magick_number_threads(image,image,image->rows,1)
3220 for (y=0; y < (ssize_t) image->rows; y++)
3226 channel_maxima = { -MagickMaximumValue, 0, 0 };
3231 if (status == MagickFalse)
3233 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3234 if (p == (
const Quantum *) NULL)
3239 channel_maxima=maxima_info;
3240 for (x=0; x < (ssize_t) image->columns; x++)
3245 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3250 PixelChannel channel = GetPixelChannelChannel(image,i);
3251 PixelTrait traits = GetPixelChannelTraits(image,channel);
3252 if ((traits & UpdatePixelTrait) == 0)
3254 pixel=(double) p[i];
3255 if (IsNaN(pixel) != 0)
3257 if (pixel > channel_maxima.maxima)
3259 channel_maxima.maxima=(double) p[i];
3264 p+=(ptrdiff_t) GetPixelChannels(image);
3266#if defined(MAGICKCORE_OPENMP_SUPPORT)
3267 #pragma omp critical (MagickCore_SIMMaximaImage)
3269 if (channel_maxima.maxima > maxima_info.maxima)
3270 maxima_info=channel_maxima;
3272 image_view=DestroyCacheView(image_view);
3273 *maxima=maxima_info.maxima;
3274 offset->x=maxima_info.x;
3275 offset->y=maxima_info.y;
3279static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3280 RectangleInfo *offset,ExceptionInfo *exception)
3299 status = MagickTrue;
3302 minima_info = { MagickMaximumValue, 0, 0 };
3310 image_view=AcquireVirtualCacheView(image,exception);
3311 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3313 if (q != (
const Quantum *) NULL)
3314 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3315#if defined(MAGICKCORE_OPENMP_SUPPORT)
3316 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3317 magick_number_threads(image,image,image->rows,1)
3319 for (y=0; y < (ssize_t) image->rows; y++)
3325 channel_minima = { MagickMaximumValue, 0, 0 };
3330 if (status == MagickFalse)
3332 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3333 if (p == (
const Quantum *) NULL)
3338 channel_minima=minima_info;
3339 for (x=0; x < (ssize_t) image->columns; x++)
3344 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3349 PixelChannel channel = GetPixelChannelChannel(image,i);
3350 PixelTrait traits = GetPixelChannelTraits(image,channel);
3351 if ((traits & UpdatePixelTrait) == 0)
3353 pixel=(double) p[i];
3354 if (IsNaN(pixel) != 0)
3356 if (pixel < channel_minima.minima)
3358 channel_minima.minima=pixel;
3363 p+=(ptrdiff_t) GetPixelChannels(image);
3365#if defined(MAGICKCORE_OPENMP_SUPPORT)
3366 #pragma omp critical (MagickCore_SIMMinimaImage)
3368 if (channel_minima.minima < minima_info.minima)
3369 minima_info=channel_minima;
3371 image_view=DestroyCacheView(image_view);
3372 *minima=minima_info.minima;
3373 offset->x=minima_info.x;
3374 offset->y=minima_info.y;
3378static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3379 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3385 status = MagickTrue;
3393 image_view=AcquireAuthenticCacheView(image,exception);
3394#if defined(MAGICKCORE_OPENMP_SUPPORT)
3395 #pragma omp parallel for schedule(static) shared(status) \
3396 magick_number_threads(image,image,image->rows,1)
3398 for (y=0; y < (ssize_t) image->rows; y++)
3406 if (status == MagickFalse)
3408 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3409 if (q == (Quantum *) NULL)
3414 for (x=0; x < (ssize_t) image->columns; x++)
3419 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3421 PixelChannel channel = GetPixelChannelChannel(image,i);
3422 PixelTrait traits = GetPixelChannelTraits(image,channel);
3423 if ((traits & UpdatePixelTrait) == 0)
3425 if (channel_statistics != (
const ChannelStatistics *) NULL)
3426 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3427 channel_statistics[channel].standard_deviation);
3429 q[i]=(Quantum) (factor*q[i]);
3431 q+=(ptrdiff_t) GetPixelChannels(image);
3433 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3436 image_view=DestroyCacheView(image_view);
3440static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3441 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3444 *alpha_fft = (Image *) NULL,
3445 *beta_fft = (Image *) NULL,
3446 *complex_multiplication = (Image *) NULL,
3447 *cross_correlation = (Image *) NULL;
3452 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3453 if (beta_fft == NULL)
3454 return((Image *) NULL);
3455 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3456 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3457 if (beta_fft == NULL)
3458 return((Image *) NULL);
3462 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3463 if (alpha_fft == (Image *) NULL)
3465 beta_fft=DestroyImageList(beta_fft);
3466 return((Image *) NULL);
3468 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3469 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3470 if (alpha_fft == (Image *) NULL)
3472 beta_fft=DestroyImageList(beta_fft);
3473 return((Image *) NULL);
3478 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3479 if (beta_fft == (Image *) NULL)
3481 alpha_fft=DestroyImageList(alpha_fft);
3482 return((Image *) NULL);
3487 AppendImageToList(&beta_fft,alpha_fft);
3488 DisableCompositeClampUnlessSpecified(beta_fft);
3489 DisableCompositeClampUnlessSpecified(beta_fft->next);
3490 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3492 beta_fft=DestroyImageList(beta_fft);
3493 if (complex_multiplication == (Image *) NULL)
3494 return((Image *) NULL);
3498 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3499 magnitude_image,0,0,exception);
3503 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3504 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3505 complex_multiplication->next,MagickFalse,exception);
3506 complex_multiplication=DestroyImageList(complex_multiplication);
3507 return(cross_correlation);
3510static MagickBooleanType SIMSetImageMean(Image *image,
3511 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3517 status = MagickTrue;
3525 image_view=AcquireAuthenticCacheView(image,exception);
3526#if defined(MAGICKCORE_OPENMP_SUPPORT)
3527 #pragma omp parallel for schedule(static) shared(status) \
3528 magick_number_threads(image,image,image->rows,1)
3530 for (y=0; y < (ssize_t) image->rows; y++)
3538 if (status == MagickFalse)
3540 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3541 if (q == (Quantum *) NULL)
3546 for (x=0; x < (ssize_t) image->columns; x++)
3551 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3553 PixelChannel channel = GetPixelChannelChannel(image,i);
3554 PixelTrait traits = GetPixelChannelTraits(image,channel);
3555 if ((traits & UpdatePixelTrait) == 0)
3557 q[i]=(Quantum) channel_statistics[channel].mean;
3559 q+=(ptrdiff_t) GetPixelChannels(image);
3561 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3564 image_view=DestroyCacheView(image_view);
3568static Image *SIMSubtractImageMean(
const Image *alpha_image,
3569 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3570 ExceptionInfo *exception)
3580 status = MagickTrue;
3588 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3589 MagickTrue,exception);
3590 if (subtract_image == (Image *) NULL)
3591 return(subtract_image);
3592 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3593 beta_view=AcquireVirtualCacheView(beta_image,exception);
3594#if defined(MAGICKCORE_OPENMP_SUPPORT)
3595 #pragma omp parallel for schedule(static) shared(status) \
3596 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3598 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3609 if (status == MagickFalse)
3611 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3612 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3614 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3619 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3624 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3626 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3627 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3628 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3629 if (((traits & UpdatePixelTrait) == 0) ||
3630 ((beta_traits & UpdatePixelTrait) == 0))
3632 if ((x >= (ssize_t) beta_image->columns) ||
3633 (y >= (ssize_t) beta_image->rows))
3636 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3637 channel_statistics[channel].mean);
3639 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3640 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3645 beta_view=DestroyCacheView(beta_view);
3646 image_view=DestroyCacheView(image_view);
3647 if (status == MagickFalse)
3648 subtract_image=DestroyImage(subtract_image);
3649 return(subtract_image);
3652static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3653 ExceptionInfo *exception)
3662 status = MagickTrue;
3670 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3671 MagickTrue,exception);
3672 if (unity_image == (Image *) NULL)
3673 return(unity_image);
3674 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3675 return(DestroyImage(unity_image));
3676 image_view=AcquireAuthenticCacheView(unity_image,exception);
3677#if defined(MAGICKCORE_OPENMP_SUPPORT)
3678 #pragma omp parallel for schedule(static) shared(status) \
3679 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3681 for (y=0; y < (ssize_t) unity_image->rows; y++)
3689 if (status == MagickFalse)
3691 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3693 if (q == (Quantum *) NULL)
3698 for (x=0; x < (ssize_t) unity_image->columns; x++)
3703 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3705 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3706 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3707 if ((traits & UpdatePixelTrait) == 0)
3709 if ((x >= (ssize_t) beta_image->columns) ||
3710 (y >= (ssize_t) beta_image->rows))
3715 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3717 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3720 image_view=DestroyCacheView(image_view);
3721 if (status == MagickFalse)
3722 unity_image=DestroyImage(unity_image);
3723 return(unity_image);
3726static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3727 ExceptionInfo *exception)
3737 status = MagickTrue;
3745 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3746 if (variance_image == (Image *) NULL)
3747 return(variance_image);
3748 image_view=AcquireAuthenticCacheView(variance_image,exception);
3749 beta_view=AcquireVirtualCacheView(beta_image,exception);
3750#if defined(MAGICKCORE_OPENMP_SUPPORT)
3751 #pragma omp parallel for schedule(static) shared(status) \
3752 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3754 for (y=0; y < (ssize_t) variance_image->rows; y++)
3765 if (status == MagickFalse)
3767 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3769 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3771 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3776 for (x=0; x < (ssize_t) variance_image->columns; x++)
3781 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3786 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3787 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3788 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3789 if (((traits & UpdatePixelTrait) == 0) ||
3790 ((beta_traits & UpdatePixelTrait) == 0))
3792 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3793 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3794 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3796 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3797 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3799 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3802 beta_view=DestroyCacheView(beta_view);
3803 image_view=DestroyCacheView(image_view);
3804 if (status == MagickFalse)
3805 variance_image=DestroyImage(variance_image);
3806 return(variance_image);
3809static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3810 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3812#define ThrowDPCSimilarityException() \
3814 if (dot_product_image != (Image *) NULL) \
3815 dot_product_image=DestroyImage(dot_product_image); \
3816 if (magnitude_image != (Image *) NULL) \
3817 magnitude_image=DestroyImage(magnitude_image); \
3818 if (reconstruct_image != (Image *) NULL) \
3819 reconstruct_image=DestroyImage(reconstruct_image); \
3820 if (rx_image != (Image *) NULL) \
3821 rx_image=DestroyImage(rx_image); \
3822 if (ry_image != (Image *) NULL) \
3823 ry_image=DestroyImage(ry_image); \
3824 if (target_image != (Image *) NULL) \
3825 target_image=DestroyImage(target_image); \
3826 if (threshold_image != (Image *) NULL) \
3827 threshold_image=DestroyImage(threshold_image); \
3828 if (trx_image != (Image *) NULL) \
3829 trx_image=DestroyImage(trx_image); \
3830 if (try_image != (Image *) NULL) \
3831 try_image=DestroyImage(try_image); \
3832 if (tx_image != (Image *) NULL) \
3833 tx_image=DestroyImage(tx_image); \
3834 if (ty_image != (Image *) NULL) \
3835 ty_image=DestroyImage(ty_image); \
3836 return((Image *) NULL); \
3843 standard_deviation = 0.0;
3846 *dot_product_image = (Image *) NULL,
3847 *magnitude_image = (Image *) NULL,
3848 *reconstruct_image = (Image *) NULL,
3849 *rx_image = (Image *) NULL,
3850 *ry_image = (Image *) NULL,
3851 *trx_image = (Image *) NULL,
3852 *target_image = (Image *) NULL,
3853 *threshold_image = (Image *) NULL,
3854 *try_image = (Image *) NULL,
3855 *tx_image = (Image *) NULL,
3856 *ty_image = (Image *) NULL;
3859 status = MagickTrue;
3867 target_image=CloneImage(image,0,0,MagickTrue,exception);
3868 if (target_image == (Image *) NULL)
3869 return((Image *) NULL);
3873 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3874 if (reconstruct_image == (Image *) NULL)
3875 ThrowDPCSimilarityException();
3879 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3881 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3882 if (rx_image == (Image *) NULL)
3883 ThrowDPCSimilarityException();
3884 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3885 reconstruct_image=DestroyImage(reconstruct_image);
3886 if (ry_image == (Image *) NULL)
3887 ThrowDPCSimilarityException();
3891 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3892 if (magnitude_image == (Image *) NULL)
3893 ThrowDPCSimilarityException();
3897 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3898 if (threshold_image == (Image *) NULL)
3899 ThrowDPCSimilarityException();
3900 status=BilevelImage(threshold_image,0.0,exception);
3901 if (status == MagickFalse)
3902 ThrowDPCSimilarityException();
3903 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3904 threshold_image=DestroyImage(threshold_image);
3905 if (status == MagickFalse)
3906 ThrowDPCSimilarityException();
3907 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
3908 reconstruct->rows)+QuantumScale;
3912 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3913 rx_image=DestroyImage(rx_image);
3914 if (trx_image == (Image *) NULL)
3915 ThrowDPCSimilarityException();
3917 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3918 magnitude_image=DestroyImage(magnitude_image);
3919 ry_image=DestroyImage(ry_image);
3920 if (try_image == (Image *) NULL)
3921 ThrowDPCSimilarityException();
3926 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
3928 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
3929 if (tx_image == (Image *) NULL)
3930 ThrowDPCSimilarityException();
3931 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
3932 target_image=DestroyImage(target_image);
3933 if (ty_image == (Image *) NULL)
3934 ThrowDPCSimilarityException();
3938 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
3939 if (magnitude_image == (Image *) NULL)
3940 ThrowDPCSimilarityException();
3944 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
3945 tx_image=DestroyImage(tx_image);
3946 if (trx_image == (Image *) NULL)
3947 ThrowDPCSimilarityException();
3949 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
3950 ty_image=DestroyImage(ty_image);
3951 magnitude_image=DestroyImage(magnitude_image);
3952 if (try_image == (Image *) NULL)
3953 ThrowDPCSimilarityException();
3958 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
3959 rx_image=DestroyImage(rx_image);
3960 tx_image=DestroyImage(tx_image);
3961 if (trx_image == (Image *) NULL)
3962 ThrowDPCSimilarityException();
3963 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
3964 ry_image=DestroyImage(ry_image);
3965 ty_image=DestroyImage(ty_image);
3966 if (try_image == (Image *) NULL)
3967 ThrowDPCSimilarityException();
3971 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
3972 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
3974 try_image=DestroyImage(try_image);
3975 if (status == MagickFalse)
3976 ThrowDPCSimilarityException();
3977 status=SIMMultiplyImage(trx_image,edge_factor,
3978 (
const ChannelStatistics *) NULL,exception);
3979 if (status == MagickFalse)
3980 ThrowDPCSimilarityException();
3984 SetGeometry(image,&geometry);
3985 geometry.width=image->columns;
3986 geometry.height=image->rows;
3987 (void) ResetImagePage(trx_image,
"0x0+0+0");
3988 dot_product_image=CropImage(trx_image,&geometry,exception);
3989 trx_image=DestroyImage(trx_image);
3990 if (dot_product_image == (Image *) NULL)
3991 ThrowDPCSimilarityException();
3992 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
3996 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
3998 if (status == MagickFalse)
3999 ThrowDPCSimilarityException();
4000 dot_product_image->depth=32;
4001 dot_product_image->colorspace=GRAYColorspace;
4002 dot_product_image->alpha_trait=UndefinedPixelTrait;
4003 status=SIMFilterImageNaNs(dot_product_image,exception);
4004 if (status == MagickFalse)
4005 ThrowDPCSimilarityException();
4006 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4007 if (status == MagickFalse)
4008 ThrowDPCSimilarityException();
4009 if ((QuantumScale*maxima) > 1.0)
4011 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4012 (
const ChannelStatistics *) NULL,exception);
4013 maxima=(double) QuantumRange;
4015 *similarity_metric=QuantumScale*maxima;
4016 return(dot_product_image);
4019static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4020 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4022#define ThrowMSESimilarityException() \
4024 if (alpha_image != (Image *) NULL) \
4025 alpha_image=DestroyImage(alpha_image); \
4026 if (beta_image != (Image *) NULL) \
4027 beta_image=DestroyImage(beta_image); \
4028 if (channel_statistics != (ChannelStatistics *) NULL) \
4029 channel_statistics=(ChannelStatistics *) \
4030 RelinquishMagickMemory(channel_statistics); \
4031 if (mean_image != (Image *) NULL) \
4032 mean_image=DestroyImage(mean_image); \
4033 if (mse_image != (Image *) NULL) \
4034 mse_image=DestroyImage(mse_image); \
4035 if (reconstruct_image != (Image *) NULL) \
4036 reconstruct_image=DestroyImage(reconstruct_image); \
4037 if (sum_image != (Image *) NULL) \
4038 sum_image=DestroyImage(sum_image); \
4039 if (alpha_image != (Image *) NULL) \
4040 alpha_image=DestroyImage(alpha_image); \
4041 return((Image *) NULL); \
4045 *channel_statistics = (ChannelStatistics *) NULL;
4051 *alpha_image = (Image *) NULL,
4052 *beta_image = (Image *) NULL,
4053 *mean_image = (Image *) NULL,
4054 *mse_image = (Image *) NULL,
4055 *reconstruct_image = (Image *) NULL,
4056 *sum_image = (Image *) NULL,
4057 *target_image = (Image *) NULL;
4060 status = MagickTrue;
4068 target_image=SIMSquareImage(image,exception);
4069 if (target_image == (Image *) NULL)
4070 ThrowMSESimilarityException();
4071 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4072 if (reconstruct_image == (Image *) NULL)
4073 ThrowMSESimilarityException();
4077 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4079 target_image=DestroyImage(target_image);
4080 if (alpha_image == (Image *) NULL)
4081 ThrowMSESimilarityException();
4082 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4083 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4084 if (status == MagickFalse)
4085 ThrowMSESimilarityException();
4089 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4090 MagickTrue,0,0,exception);
4091 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4092 if (beta_image == (Image *) NULL)
4094 reconstruct_image=DestroyImage(reconstruct_image);
4095 ThrowMSESimilarityException();
4097 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4098 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4099 reconstruct_image=DestroyImage(reconstruct_image);
4100 if (status == MagickFalse)
4101 ThrowMSESimilarityException();
4105 sum_image=SIMSquareImage(reconstruct,exception);
4106 if (sum_image == (Image *) NULL)
4107 ThrowMSESimilarityException();
4108 channel_statistics=GetImageStatistics(sum_image,exception);
4109 if (channel_statistics == (ChannelStatistics *) NULL)
4110 ThrowMSESimilarityException();
4111 status=SetImageStorageClass(sum_image,DirectClass,exception);
4112 if (status == MagickFalse)
4113 ThrowMSESimilarityException();
4114 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4115 channel_statistics=(ChannelStatistics *)
4116 RelinquishMagickMemory(channel_statistics);
4117 if (status == MagickFalse)
4118 ThrowMSESimilarityException();
4122 AppendImageToList(&sum_image,alpha_image);
4123 AppendImageToList(&sum_image,beta_image);
4124 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4125 if (mean_image == (Image *) NULL)
4126 ThrowMSESimilarityException();
4127 sum_image=DestroyImage(sum_image);
4128 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4129 if (status == MagickFalse)
4130 ThrowMSESimilarityException();
4134 SetGeometry(image,&geometry);
4135 geometry.width=image->columns;
4136 geometry.height=image->rows;
4137 (void) ResetImagePage(mean_image,
"0x0+0+0");
4138 mse_image=CropImage(mean_image,&geometry,exception);
4139 mean_image=DestroyImage(mean_image);
4140 if (mse_image == (Image *) NULL)
4141 ThrowMSESimilarityException();
4145 (void) ResetImagePage(mse_image,
"0x0+0+0");
4146 (void) ClampImage(mse_image,exception);
4147 mse_image->depth=32;
4148 mse_image->colorspace=GRAYColorspace;
4149 mse_image->alpha_trait=UndefinedPixelTrait;
4150 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4151 if (status == MagickFalse)
4152 ThrowMSESimilarityException();
4153 status=NegateImage(mse_image,MagickFalse,exception);
4154 if (status == MagickFalse)
4155 ThrowMSESimilarityException();
4156 alpha_image=DestroyImage(alpha_image);
4157 beta_image=DestroyImage(beta_image);
4158 if ((QuantumScale*minima) < FLT_EPSILON)
4160 *similarity_metric=QuantumScale*minima;
4164static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4165 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4167#define ThrowNCCSimilarityException() \
4169 if (alpha_image != (Image *) NULL) \
4170 alpha_image=DestroyImage(alpha_image); \
4171 if (beta_image != (Image *) NULL) \
4172 beta_image=DestroyImage(beta_image); \
4173 if (channel_statistics != (ChannelStatistics *) NULL) \
4174 channel_statistics=(ChannelStatistics *) \
4175 RelinquishMagickMemory(channel_statistics); \
4176 if (correlation_image != (Image *) NULL) \
4177 correlation_image=DestroyImage(correlation_image); \
4178 if (divide_image != (Image *) NULL) \
4179 divide_image=DestroyImage(divide_image); \
4180 if (ncc_image != (Image *) NULL) \
4181 ncc_image=DestroyImage(ncc_image); \
4182 if (normalize_image != (Image *) NULL) \
4183 normalize_image=DestroyImage(normalize_image); \
4184 if (reconstruct_image != (Image *) NULL) \
4185 reconstruct_image=DestroyImage(reconstruct_image); \
4186 if (target_image != (Image *) NULL) \
4187 target_image=DestroyImage(target_image); \
4188 if (variance_image != (Image *) NULL) \
4189 variance_image=DestroyImage(variance_image); \
4190 return((Image *) NULL); \
4194 *channel_statistics = (ChannelStatistics *) NULL;
4200 *alpha_image = (Image *) NULL,
4201 *beta_image = (Image *) NULL,
4202 *correlation_image = (Image *) NULL,
4203 *divide_image = (Image *) NULL,
4204 *ncc_image = (Image *) NULL,
4205 *normalize_image = (Image *) NULL,
4206 *reconstruct_image = (Image *) NULL,
4207 *target_image = (Image *) NULL,
4208 *variance_image = (Image *) NULL;
4211 status = MagickTrue;
4219 target_image=SIMSquareImage(image,exception);
4220 if (target_image == (Image *) NULL)
4221 ThrowNCCSimilarityException();
4222 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4223 if (reconstruct_image == (Image *) NULL)
4224 ThrowNCCSimilarityException();
4228 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4230 target_image=DestroyImage(target_image);
4231 if (alpha_image == (Image *) NULL)
4232 ThrowNCCSimilarityException();
4233 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4234 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4236 if (status == MagickFalse)
4237 ThrowNCCSimilarityException();
4241 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4242 reconstruct_image=DestroyImage(reconstruct_image);
4243 if (beta_image == (Image *) NULL)
4244 ThrowNCCSimilarityException();
4245 target_image=SIMSquareImage(beta_image,exception);
4246 beta_image=DestroyImage(beta_image);
4247 if (target_image == (Image *) NULL)
4248 ThrowNCCSimilarityException();
4249 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4250 (
const ChannelStatistics *) NULL,exception);
4251 if (status == MagickFalse)
4252 ThrowNCCSimilarityException();
4256 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4257 target_image=DestroyImage(target_image);
4258 alpha_image=DestroyImage(alpha_image);
4259 if (variance_image == (Image *) NULL)
4260 ThrowNCCSimilarityException();
4264 channel_statistics=GetImageStatistics(reconstruct,exception);
4265 if (channel_statistics == (ChannelStatistics *) NULL)
4266 ThrowNCCSimilarityException();
4267 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4268 if (status == MagickFalse)
4269 ThrowNCCSimilarityException();
4270 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4272 channel_statistics=(ChannelStatistics *)
4273 RelinquishMagickMemory(channel_statistics);
4274 if (normalize_image == (Image *) NULL)
4275 ThrowNCCSimilarityException();
4276 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4277 normalize_image=DestroyImage(normalize_image);
4278 if (correlation_image == (Image *) NULL)
4279 ThrowNCCSimilarityException();
4283 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4284 correlation_image=DestroyImage(correlation_image);
4285 variance_image=DestroyImage(variance_image);
4286 if (divide_image == (Image *) NULL)
4287 ThrowNCCSimilarityException();
4291 SetGeometry(image,&geometry);
4292 geometry.width=image->columns;
4293 geometry.height=image->rows;
4294 (void) ResetImagePage(divide_image,
"0x0+0+0");
4295 ncc_image=CropImage(divide_image,&geometry,exception);
4296 divide_image=DestroyImage(divide_image);
4297 if (ncc_image == (Image *) NULL)
4298 ThrowNCCSimilarityException();
4302 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4303 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4304 if (status == MagickFalse)
4305 ThrowNCCSimilarityException();
4306 ncc_image->depth=32;
4307 ncc_image->colorspace=GRAYColorspace;
4308 ncc_image->alpha_trait=UndefinedPixelTrait;
4309 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4310 if (status == MagickFalse)
4311 ThrowNCCSimilarityException();
4312 if ((QuantumScale*maxima) > 1.0)
4314 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4315 (
const ChannelStatistics *) NULL,exception);
4316 maxima=(double) QuantumRange;
4318 *similarity_metric=QuantumScale*maxima;
4322static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4323 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4325#define ThrowPhaseSimilarityException() \
4327 if (correlation_image != (Image *) NULL) \
4328 correlation_image=DestroyImage(correlation_image); \
4329 if (fft_images != (Image *) NULL) \
4330 fft_images=DestroyImageList(fft_images); \
4331 if (gamma_image != (Image *) NULL) \
4332 gamma_image=DestroyImage(gamma_image); \
4333 if (magnitude_image != (Image *) NULL) \
4334 magnitude_image=DestroyImage(magnitude_image); \
4335 if (phase_image != (Image *) NULL) \
4336 phase_image=DestroyImage(phase_image); \
4337 if (reconstruct_image != (Image *) NULL) \
4338 reconstruct_image=DestroyImage(reconstruct_image); \
4339 if (reconstruct_magnitude != (Image *) NULL) \
4340 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4341 if (target_image != (Image *) NULL) \
4342 target_image=DestroyImage(target_image); \
4343 if (test_magnitude != (Image *) NULL) \
4344 test_magnitude=DestroyImage(test_magnitude); \
4345 return((Image *) NULL); \
4352 *correlation_image = (Image *) NULL,
4353 *fft_images = (Image *) NULL,
4354 *gamma_image = (Image *) NULL,
4355 *magnitude_image = (Image *) NULL,
4356 *phase_image = (Image *) NULL,
4357 *reconstruct_image = (Image *) NULL,
4358 *reconstruct_magnitude = (Image *) NULL,
4359 *target_image = (Image *) NULL,
4360 *test_magnitude = (Image *) NULL;
4363 status = MagickTrue;
4371 target_image=CloneImage(image,0,0,MagickTrue,exception);
4372 if (target_image == (Image *) NULL)
4373 ThrowPhaseSimilarityException();
4374 (void) ResetImagePage(target_image,
"0x0+0+0");
4375 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4376 &target_image->background_color);
4377 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4378 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4379 if (status == MagickFalse)
4380 ThrowPhaseSimilarityException();
4384 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4385 if (reconstruct_image == (Image *) NULL)
4386 ThrowPhaseSimilarityException();
4387 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4388 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4389 &reconstruct_image->background_color);
4390 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4391 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4392 if (status == MagickFalse)
4393 ThrowPhaseSimilarityException();
4397 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4398 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4399 if (fft_images == (Image *) NULL)
4400 ThrowPhaseSimilarityException();
4401 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4402 fft_images=DestroyImageList(fft_images);
4403 if (test_magnitude == (Image *) NULL)
4404 ThrowPhaseSimilarityException();
4405 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4406 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4408 if (fft_images == (Image *) NULL)
4409 ThrowPhaseSimilarityException();
4410 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4411 fft_images=DestroyImageList(fft_images);
4412 if (reconstruct_magnitude == (Image *) NULL)
4413 ThrowPhaseSimilarityException();
4414 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4415 if (magnitude_image == (Image *) NULL)
4416 ThrowPhaseSimilarityException();
4417 DisableCompositeClampUnlessSpecified(magnitude_image);
4418 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4419 MagickTrue,0,0,exception);
4423 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4424 magnitude_image,exception);
4425 target_image=DestroyImage(target_image);
4426 reconstruct_image=DestroyImage(reconstruct_image);
4427 test_magnitude=DestroyImage(test_magnitude);
4428 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4429 if (correlation_image == (Image *) NULL)
4430 ThrowPhaseSimilarityException();
4434 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4435 correlation_image=DestroyImage(correlation_image);
4436 if (gamma_image == (Image *) NULL)
4437 ThrowPhaseSimilarityException();
4441 SetGeometry(image,&geometry);
4442 geometry.width=image->columns;
4443 geometry.height=image->rows;
4444 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4445 phase_image=CropImage(gamma_image,&geometry,exception);
4446 gamma_image=DestroyImage(gamma_image);
4447 if (phase_image == (Image *) NULL)
4448 ThrowPhaseSimilarityException();
4449 (void) ResetImagePage(phase_image,
"0x0+0+0");
4453 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4454 if (status == MagickFalse)
4455 ThrowPhaseSimilarityException();
4456 phase_image->depth=32;
4457 phase_image->colorspace=GRAYColorspace;
4458 phase_image->alpha_trait=UndefinedPixelTrait;
4459 status=SIMFilterImageNaNs(phase_image,exception);
4460 if (status == MagickFalse)
4461 ThrowPhaseSimilarityException();
4462 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4463 if (status == MagickFalse)
4464 ThrowPhaseSimilarityException();
4465 magnitude_image=DestroyImage(magnitude_image);
4466 if ((QuantumScale*maxima) > 1.0)
4468 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4469 (
const ChannelStatistics *) NULL,exception);
4470 maxima=(double) QuantumRange;
4472 *similarity_metric=QuantumScale*maxima;
4473 return(phase_image);
4476static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4477 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4480 *psnr_image = (Image *) NULL;
4482 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4484 if (psnr_image == (Image *) NULL)
4486 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4487 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4491static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4492 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4495 *rmse_image = (Image *) NULL;
4497 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4499 if (rmse_image == (Image *) NULL)
4501 *similarity_metric=sqrt(*similarity_metric);
4506static double GetSimilarityMetric(
const Image *image,
4507 const Image *reconstruct_image,
const MetricType metric,
4508 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4511 *channel_similarity,
4515 *sans_exception = AcquireExceptionInfo();
4521 status = MagickTrue;
4527 length = MaxPixelChannels+1UL;
4529 SetGeometry(reconstruct_image,&geometry);
4530 geometry.x=x_offset;
4531 geometry.y=y_offset;
4532 similarity_image=CropImage(image,&geometry,sans_exception);
4533 sans_exception=DestroyExceptionInfo(sans_exception);
4534 if (similarity_image == (Image *) NULL)
4539 channel_similarity=(
double *) AcquireQuantumMemory(length,
4540 sizeof(*channel_similarity));
4541 if (channel_similarity == (
double *) NULL)
4542 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4543 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4546 case AbsoluteErrorMetric:
4548 status=GetAESimilarity(similarity_image,reconstruct_image,
4549 channel_similarity,exception);
4552 case DotProductCorrelationErrorMetric:
4554 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4555 channel_similarity,exception);
4558 case FuzzErrorMetric:
4560 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4561 channel_similarity,exception);
4564 case MeanAbsoluteErrorMetric:
4566 status=GetMAESimilarity(similarity_image,reconstruct_image,
4567 channel_similarity,exception);
4570 case MeanErrorPerPixelErrorMetric:
4572 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4573 channel_similarity,exception);
4576 case MeanSquaredErrorMetric:
4578 status=GetMSESimilarity(similarity_image,reconstruct_image,
4579 channel_similarity,exception);
4582 case NormalizedCrossCorrelationErrorMetric:
4584 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4585 channel_similarity,exception);
4588 case PeakAbsoluteErrorMetric:
4590 status=GetPASimilarity(similarity_image,reconstruct_image,
4591 channel_similarity,exception);
4594 case PeakSignalToNoiseRatioErrorMetric:
4596 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4597 channel_similarity,exception);
4600 case PerceptualHashErrorMetric:
4602 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4603 channel_similarity,exception);
4606 case PhaseCorrelationErrorMetric:
4608 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4609 channel_similarity,exception);
4612 case PixelDifferenceCountErrorMetric:
4614 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4615 channel_similarity,exception);
4618 case RootMeanSquaredErrorMetric:
4619 case UndefinedErrorMetric:
4622 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4623 channel_similarity,exception);
4626 case StructuralDissimilarityErrorMetric:
4628 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4629 channel_similarity,exception);
4632 case StructuralSimilarityErrorMetric:
4634 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4635 channel_similarity,exception);
4639 similarity_image=DestroyImage(similarity_image);
4640 similarity=channel_similarity[CompositePixelChannel];
4641 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4642 if (status == MagickFalse)
4647MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4648 const MetricType metric,
const double similarity_threshold,
4649 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4651#define SimilarityImageTag "Similarity/Image"
4667 *similarity_image = (Image *) NULL;
4670 status = MagickTrue;
4676 similarity_info = { 0.0, 0, 0 };
4685 assert(image != (
const Image *) NULL);
4686 assert(image->signature == MagickCoreSignature);
4687 assert(exception != (ExceptionInfo *) NULL);
4688 assert(exception->signature == MagickCoreSignature);
4689 assert(offset != (RectangleInfo *) NULL);
4690 if (IsEventLogging() != MagickFalse)
4691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4692 SetGeometry(reconstruct,offset);
4693 *similarity_metric=0.0;
4696#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4698 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4699 if (artifact == (
const char *) NULL)
4700 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4701 if (((artifact == (
const char *) NULL) ||
4702 (IsStringTrue(artifact) != MagickFalse)) &&
4703 ((image->channels & ReadMaskChannel) == 0))
4706 case DotProductCorrelationErrorMetric:
4708 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4709 similarity_metric,exception);
4710 return(similarity_image);
4712 case MeanSquaredErrorMetric:
4714 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4715 similarity_metric,exception);
4716 return(similarity_image);
4718 case NormalizedCrossCorrelationErrorMetric:
4720 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4721 similarity_metric,exception);
4722 return(similarity_image);
4724 case PeakSignalToNoiseRatioErrorMetric:
4726 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4727 similarity_metric,exception);
4728 return(similarity_image);
4730 case PhaseCorrelationErrorMetric:
4732 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4733 similarity_metric,exception);
4734 return(similarity_image);
4736 case RootMeanSquaredErrorMetric:
4737 case UndefinedErrorMetric:
4739 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4740 similarity_metric,exception);
4741 return(similarity_image);
4748 if ((image->columns < reconstruct->columns) ||
4749 (image->rows < reconstruct->rows))
4751 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4752 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4753 return((Image *) NULL);
4755 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4756 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4757 if (similarity_image == (Image *) NULL)
4758 return((Image *) NULL);
4759 similarity_image->depth=32;
4760 similarity_image->colorspace=GRAYColorspace;
4761 similarity_image->alpha_trait=UndefinedPixelTrait;
4762 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4763 if (status == MagickFalse)
4764 return(DestroyImage(similarity_image));
4768 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4769 similarity_info.x,similarity_info.y,exception);
4770 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4771#if defined(MAGICKCORE_OPENMP_SUPPORT)
4772 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4773 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4775 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4781 threshold_trigger = MagickFalse;
4787 channel_info = similarity_info;
4792 if (status == MagickFalse)
4794 if (threshold_trigger != MagickFalse)
4796 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4797 similarity_image->columns,1,exception);
4798 if (q == (Quantum *) NULL)
4803 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4808 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4812 case DotProductCorrelationErrorMetric:
4813 case NormalizedCrossCorrelationErrorMetric:
4814 case PeakSignalToNoiseRatioErrorMetric:
4815 case PhaseCorrelationErrorMetric:
4816 case StructuralSimilarityErrorMetric:
4818 if (similarity <= channel_info.similarity)
4820 channel_info.similarity=similarity;
4827 if (similarity >= channel_info.similarity)
4829 channel_info.similarity=similarity;
4835 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4837 PixelChannel channel = GetPixelChannelChannel(image,i);
4838 PixelTrait traits = GetPixelChannelTraits(image,channel);
4839 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4841 if (((traits & UpdatePixelTrait) == 0) ||
4842 ((similarity_traits & UpdatePixelTrait) == 0))
4846 case DotProductCorrelationErrorMetric:
4847 case NormalizedCrossCorrelationErrorMetric:
4848 case PeakSignalToNoiseRatioErrorMetric:
4849 case PhaseCorrelationErrorMetric:
4850 case StructuralSimilarityErrorMetric:
4852 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4853 QuantumRange*similarity),q);
4858 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4859 QuantumRange*(1.0-similarity)),q);
4864 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4866#if defined(MAGICKCORE_OPENMP_SUPPORT)
4867 #pragma omp critical (MagickCore_GetSimilarityMetric)
4871 case DotProductCorrelationErrorMetric:
4872 case NormalizedCrossCorrelationErrorMetric:
4873 case PeakSignalToNoiseRatioErrorMetric:
4874 case PhaseCorrelationErrorMetric:
4875 case StructuralSimilarityErrorMetric:
4877 if (similarity_threshold != DefaultSimilarityThreshold)
4878 if (channel_info.similarity >= similarity_threshold)
4879 threshold_trigger=MagickTrue;
4880 if (channel_info.similarity >= similarity_info.similarity)
4881 similarity_info=channel_info;
4886 if (similarity_threshold != DefaultSimilarityThreshold)
4887 if (channel_info.similarity < similarity_threshold)
4888 threshold_trigger=MagickTrue;
4889 if (channel_info.similarity < similarity_info.similarity)
4890 similarity_info=channel_info;
4894 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4896 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4902 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4903 if (proceed == MagickFalse)
4907 similarity_view=DestroyCacheView(similarity_view);
4908 if (status == MagickFalse)
4909 similarity_image=DestroyImage(similarity_image);
4910 *similarity_metric=similarity_info.similarity;
4911 if (fabs(*similarity_metric) < MagickEpsilon)
4912 *similarity_metric=0.0;
4913 offset->x=similarity_info.x;
4914 offset->y=similarity_info.y;
4915 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4916 GetMagickPrecision(),*similarity_metric);
4917 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4918 GetMagickPrecision(),(
double) offset->x);
4919 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4920 GetMagickPrecision(),(
double) offset->y);
4921 return(similarity_image);