43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/channel.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/compare.h"
54#include "MagickCore/compare-private.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/fourier.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/registry.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/statistic.h"
75#include "MagickCore/statistic-private.h"
76#include "MagickCore/string-private.h"
77#include "MagickCore/thread-private.h"
78#include "MagickCore/threshold.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/utility.h"
81#include "MagickCore/version.h"
115MagickExport Image *CompareImages(Image *image,
const Image *reconstruct_image,
116 const MetricType metric,
double *distortion,ExceptionInfo *exception)
149 assert(image != (Image *) NULL);
150 assert(image->signature == MagickCoreSignature);
151 assert(reconstruct_image != (
const Image *) NULL);
152 assert(reconstruct_image->signature == MagickCoreSignature);
153 assert(distortion != (
double *) NULL);
154 if (IsEventLogging() != MagickFalse)
155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
157 status=GetImageDistortion(image,reconstruct_image,metric,distortion,
159 if (status == MagickFalse)
160 return((Image *) NULL);
161 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
162 SetGeometry(image,&geometry);
163 geometry.width=columns;
164 geometry.height=rows;
165 clone_image=CloneImage(image,0,0,MagickTrue,exception);
166 if (clone_image == (Image *) NULL)
167 return((Image *) NULL);
168 (void) SetImageMask(clone_image,ReadPixelMask,(Image *) NULL,exception);
169 difference_image=ExtentImage(clone_image,&geometry,exception);
170 clone_image=DestroyImage(clone_image);
171 if (difference_image == (Image *) NULL)
172 return((Image *) NULL);
173 (void) ResetImagePage(difference_image,
"0x0+0+0");
174 (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel,exception);
175 highlight_image=CloneImage(image,columns,rows,MagickTrue,exception);
176 if (highlight_image == (Image *) NULL)
178 difference_image=DestroyImage(difference_image);
179 return((Image *) NULL);
181 status=SetImageStorageClass(highlight_image,DirectClass,exception);
182 if (status == MagickFalse)
184 difference_image=DestroyImage(difference_image);
185 highlight_image=DestroyImage(highlight_image);
186 return((Image *) NULL);
188 (void) SetImageMask(highlight_image,ReadPixelMask,(Image *) NULL,exception);
189 (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel,exception);
190 (void) QueryColorCompliance(
"#f1001ecc",AllCompliance,&highlight,exception);
191 artifact=GetImageArtifact(image,
"compare:highlight-color");
192 if (artifact != (
const char *) NULL)
193 (void) QueryColorCompliance(artifact,AllCompliance,&highlight,exception);
194 (void) QueryColorCompliance(
"#ffffffcc",AllCompliance,&lowlight,exception);
195 artifact=GetImageArtifact(image,
"compare:lowlight-color");
196 if (artifact != (
const char *) NULL)
197 (void) QueryColorCompliance(artifact,AllCompliance,&lowlight,exception);
198 (void) QueryColorCompliance(
"#888888cc",AllCompliance,&masklight,exception);
199 artifact=GetImageArtifact(image,
"compare:masklight-color");
200 if (artifact != (
const char *) NULL)
201 (void) QueryColorCompliance(artifact,AllCompliance,&masklight,exception);
205 image_view=AcquireVirtualCacheView(image,exception);
206 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
207 highlight_view=AcquireAuthenticCacheView(highlight_image,exception);
208#if defined(MAGICKCORE_OPENMP_SUPPORT)
209 #pragma omp parallel for schedule(static) shared(status) \
210 magick_number_threads(image,highlight_image,rows,1)
212 for (y=0; y < (ssize_t) rows; y++)
227 if (status == MagickFalse)
229 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
230 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
231 r=QueueCacheViewAuthenticPixels(highlight_view,0,y,columns,1,exception);
232 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL) ||
233 (r == (Quantum *) NULL))
238 for (x=0; x < (ssize_t) columns; x++)
240 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
241 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
243 SetPixelViaPixelInfo(highlight_image,&masklight,r);
244 p+=(ptrdiff_t) GetPixelChannels(image);
245 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
246 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
249 if (IsFuzzyEquivalencePixel(image,p,reconstruct_image,q) == MagickFalse)
250 SetPixelViaPixelInfo(highlight_image,&highlight,r);
252 SetPixelViaPixelInfo(highlight_image,&lowlight,r);
253 p+=(ptrdiff_t) GetPixelChannels(image);
254 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
255 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
257 sync=SyncCacheViewAuthenticPixels(highlight_view,exception);
258 if (sync == MagickFalse)
261 highlight_view=DestroyCacheView(highlight_view);
262 reconstruct_view=DestroyCacheView(reconstruct_view);
263 image_view=DestroyCacheView(image_view);
264 if ((status != MagickFalse) && (difference_image != (Image *) NULL))
265 status=CompositeImage(difference_image,highlight_image,image->compose,
266 MagickTrue,0,0,exception);
267 highlight_image=DestroyImage(highlight_image);
268 if (status == MagickFalse)
269 difference_image=DestroyImage(difference_image);
270 return(difference_image);
307static MagickBooleanType GetAESimilarity(
const Image *image,
308 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
333 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
334 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
335 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
336 image_view=AcquireVirtualCacheView(image,exception);
337 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
338#if defined(MAGICKCORE_OPENMP_SUPPORT)
339 #pragma omp parallel for schedule(static) shared(similarity,status) \
340 magick_number_threads(image,image,rows,1)
342 for (y=0; y < (ssize_t) rows; y++)
349 channel_similarity[MaxPixelChannels+1] = { 0.0 };
354 if (status == MagickFalse)
356 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
357 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
358 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
363 for (x=0; x < (ssize_t) columns; x++)
372 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
373 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
375 p+=(ptrdiff_t) GetPixelChannels(image);
376 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
379 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
380 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
381 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
386 PixelChannel channel = GetPixelChannelChannel(image,i);
387 PixelTrait traits = GetPixelChannelTraits(image,channel);
388 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
390 if (((traits & UpdatePixelTrait) == 0) ||
391 ((reconstruct_traits & UpdatePixelTrait) == 0))
393 if (channel == AlphaPixelChannel)
394 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
397 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
398 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
400 double ae = fabs(QuantumScale*error);
401 channel_similarity[i]+=ae;
402 channel_similarity[CompositePixelChannel]+=ae;
405 p+=(ptrdiff_t) GetPixelChannels(image);
406 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
408#if defined(MAGICKCORE_OPENMP_SUPPORT)
409 #pragma omp critical (MagickCore_GetAESimilarity)
415 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
417 PixelChannel channel = GetPixelChannelChannel(image,j);
418 PixelTrait traits = GetPixelChannelTraits(image,channel);
419 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
421 if (((traits & UpdatePixelTrait) == 0) ||
422 ((reconstruct_traits & UpdatePixelTrait) == 0))
424 similarity[j]+=channel_similarity[j];
426 similarity[CompositePixelChannel]+=
427 channel_similarity[CompositePixelChannel];
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 PixelChannel channel = GetPixelChannelChannel(image,k);
436 PixelTrait traits = GetPixelChannelTraits(image,channel);
437 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
439 if (((traits & UpdatePixelTrait) == 0) ||
440 ((reconstruct_traits & UpdatePixelTrait) == 0))
445 similarity[CompositePixelChannel]*=area;
447 similarity[CompositePixelChannel]/=(double) channels;
451static MagickBooleanType GetDPCSimilarity(
const Image *image,
452 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
454#define SimilarityImageTag "Similarity/Image"
462 *reconstruct_statistics;
465 norm[MaxPixelChannels+1] = { 0.0 },
466 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
485 image_statistics=GetImageStatistics(image,exception);
486 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
487 if ((image_statistics == (ChannelStatistics *) NULL) ||
488 (reconstruct_statistics == (ChannelStatistics *) NULL))
490 if (image_statistics != (ChannelStatistics *) NULL)
491 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
493 if (reconstruct_statistics != (ChannelStatistics *) NULL)
494 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
495 reconstruct_statistics);
498 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
499 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
500 image_view=AcquireVirtualCacheView(image,exception);
501 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
502#if defined(MAGICKCORE_OPENMP_SUPPORT)
503 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
504 magick_number_threads(image,image,rows,1)
506 for (y=0; y < (ssize_t) rows; y++)
513 channel_norm[MaxPixelChannels+1] = { 0.0 },
514 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
515 channel_similarity[MaxPixelChannels+1] = { 0.0 };
520 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
521 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
522 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
527 for (x=0; x < (ssize_t) columns; x++)
536 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
537 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
539 p+=(ptrdiff_t) GetPixelChannels(image);
540 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
543 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
544 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
545 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
551 PixelChannel channel = GetPixelChannelChannel(image,i);
552 PixelTrait traits = GetPixelChannelTraits(image,channel);
553 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
555 if (((traits & UpdatePixelTrait) == 0) ||
556 ((reconstruct_traits & UpdatePixelTrait) == 0))
558 if (channel == AlphaPixelChannel)
560 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
561 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
562 channel,q)-reconstruct_statistics[channel].mean);
566 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
567 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
568 q)-reconstruct_statistics[channel].mean);
570 channel_similarity[i]+=alpha*beta;
571 channel_norm[i]+=alpha*alpha;
572 channel_reconstruct_norm[i]+=beta*beta;
574 p+=(ptrdiff_t) GetPixelChannels(image);
575 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
577#if defined(MAGICKCORE_OPENMP_SUPPORT)
578 #pragma omp critical (MagickCore_GetDPCSimilarity)
584 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
586 PixelChannel channel = GetPixelChannelChannel(image,j);
587 PixelTrait traits = GetPixelChannelTraits(image,channel);
588 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
590 if (((traits & UpdatePixelTrait) == 0) ||
591 ((reconstruct_traits & UpdatePixelTrait) == 0))
593 similarity[j]+=channel_similarity[j];
594 similarity[CompositePixelChannel]+=channel_similarity[j];
595 norm[j]+=channel_norm[j];
596 norm[CompositePixelChannel]+=channel_norm[j];
597 reconstruct_norm[j]+=channel_reconstruct_norm[j];
598 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
601 if (image->progress_monitor != (MagickProgressMonitor) NULL)
606#if defined(MAGICKCORE_OPENMP_SUPPORT)
610 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
611 if (proceed == MagickFalse)
618 reconstruct_view=DestroyCacheView(reconstruct_view);
619 image_view=DestroyCacheView(image_view);
623 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
625 PixelChannel channel = GetPixelChannelChannel(image,k);
626 PixelTrait traits = GetPixelChannelTraits(image,channel);
627 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
629 if (((traits & UpdatePixelTrait) == 0) ||
630 ((reconstruct_traits & UpdatePixelTrait) == 0))
632 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
634 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
635 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
639 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
640 reconstruct_statistics);
641 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
646static MagickBooleanType GetFUZZSimilarity(
const Image *image,
647 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
671 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
672 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
673 image_view=AcquireVirtualCacheView(image,exception);
674 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
675#if defined(MAGICKCORE_OPENMP_SUPPORT)
676 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
677 magick_number_threads(image,image,rows,1)
679 for (y=0; y < (ssize_t) rows; y++)
687 channel_similarity[MaxPixelChannels+1] = { 0.0 };
692 if (status == MagickFalse)
694 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
695 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
696 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
701 for (x=0; x < (ssize_t) columns; x++)
710 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
711 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
713 p+=(ptrdiff_t) GetPixelChannels(image);
714 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
717 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
718 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
719 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
724 PixelChannel channel = GetPixelChannelChannel(image,i);
725 PixelTrait traits = GetPixelChannelTraits(image,channel);
726 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
728 if (((traits & UpdatePixelTrait) == 0) ||
729 ((reconstruct_traits & UpdatePixelTrait) == 0))
731 if (channel == AlphaPixelChannel)
732 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
735 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
736 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
738 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
739 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
744 p+=(ptrdiff_t) GetPixelChannels(image);
745 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
747#if defined(MAGICKCORE_OPENMP_SUPPORT)
748 #pragma omp critical (MagickCore_GetFUZZSimilarity)
755 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
757 PixelChannel channel = GetPixelChannelChannel(image,j);
758 PixelTrait traits = GetPixelChannelTraits(image,channel);
759 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
761 if (((traits & UpdatePixelTrait) == 0) ||
762 ((reconstruct_traits & UpdatePixelTrait) == 0))
764 similarity[j]+=channel_similarity[j];
766 similarity[CompositePixelChannel]+=
767 channel_similarity[CompositePixelChannel];
770 reconstruct_view=DestroyCacheView(reconstruct_view);
771 image_view=DestroyCacheView(image_view);
772 area=MagickSafeReciprocal(area);
773 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
775 PixelChannel channel = GetPixelChannelChannel(image,k);
776 PixelTrait traits = GetPixelChannelTraits(image,channel);
777 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
779 if (((traits & UpdatePixelTrait) == 0) ||
780 ((reconstruct_traits & UpdatePixelTrait) == 0))
784 similarity[CompositePixelChannel]*=area;
788static MagickBooleanType GetMAESimilarity(
const Image *image,
789 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
813 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
814 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
815 image_view=AcquireVirtualCacheView(image,exception);
816 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
817#if defined(MAGICKCORE_OPENMP_SUPPORT)
818 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
819 magick_number_threads(image,image,rows,1)
821 for (y=0; y < (ssize_t) rows; y++)
829 channel_similarity[MaxPixelChannels+1] = { 0.0 };
834 if (status == MagickFalse)
836 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
837 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
838 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
843 for (x=0; x < (ssize_t) columns; x++)
852 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
853 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
855 p+=(ptrdiff_t) GetPixelChannels(image);
856 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
859 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
860 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
861 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
866 PixelChannel channel = GetPixelChannelChannel(image,i);
867 PixelTrait traits = GetPixelChannelTraits(image,channel);
868 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
870 if (((traits & UpdatePixelTrait) == 0) ||
871 ((reconstruct_traits & UpdatePixelTrait) == 0))
873 if (channel == AlphaPixelChannel)
874 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
875 reconstruct_image,channel,q));
877 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
879 channel_similarity[i]+=error;
880 channel_similarity[CompositePixelChannel]+=error;
883 p+=(ptrdiff_t) GetPixelChannels(image);
884 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
886#if defined(MAGICKCORE_OPENMP_SUPPORT)
887 #pragma omp critical (MagickCore_GetMAESimilarity)
894 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
896 PixelChannel channel = GetPixelChannelChannel(image,j);
897 PixelTrait traits = GetPixelChannelTraits(image,channel);
898 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
900 if (((traits & UpdatePixelTrait) == 0) ||
901 ((reconstruct_traits & UpdatePixelTrait) == 0))
903 similarity[j]+=channel_similarity[j];
905 similarity[CompositePixelChannel]+=
906 channel_similarity[CompositePixelChannel];
909 reconstruct_view=DestroyCacheView(reconstruct_view);
910 image_view=DestroyCacheView(image_view);
911 area=MagickSafeReciprocal(area);
912 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
914 PixelChannel channel = GetPixelChannelChannel(image,k);
915 PixelTrait traits = GetPixelChannelTraits(image,channel);
916 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
918 if (((traits & UpdatePixelTrait) == 0) ||
919 ((reconstruct_traits & UpdatePixelTrait) == 0))
924 similarity[CompositePixelChannel]*=area;
926 similarity[CompositePixelChannel]/=(double) channels;
930static MagickBooleanType GetMEPPSimilarity(Image *image,
931 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
939 maximum_error = -MagickMaximumValue,
957 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
958 image_view=AcquireVirtualCacheView(image,exception);
959 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
960#if defined(MAGICKCORE_OPENMP_SUPPORT)
961 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
962 magick_number_threads(image,image,rows,1)
964 for (y=0; y < (ssize_t) rows; y++)
972 channel_similarity[MaxPixelChannels+1] = { 0.0 },
973 channel_maximum_error = maximum_error,
974 channel_mean_error = 0.0;
979 if (status == MagickFalse)
981 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
982 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
983 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
988 for (x=0; x < (ssize_t) columns; x++)
997 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
998 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1000 p+=(ptrdiff_t) GetPixelChannels(image);
1001 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1004 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1005 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1006 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1011 PixelChannel channel = GetPixelChannelChannel(image,i);
1012 PixelTrait traits = GetPixelChannelTraits(image,channel);
1013 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1015 if (((traits & UpdatePixelTrait) == 0) ||
1016 ((reconstruct_traits & UpdatePixelTrait) == 0))
1018 if (channel == AlphaPixelChannel)
1019 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
1020 reconstruct_image,channel,q));
1022 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1024 channel_similarity[i]+=error;
1025 channel_similarity[CompositePixelChannel]+=error;
1026 channel_mean_error+=error*error;
1027 if (error > channel_maximum_error)
1028 channel_maximum_error=error;
1031 p+=(ptrdiff_t) GetPixelChannels(image);
1032 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1034#if defined(MAGICKCORE_OPENMP_SUPPORT)
1035 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1042 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1044 PixelChannel channel = GetPixelChannelChannel(image,j);
1045 PixelTrait traits = GetPixelChannelTraits(image,channel);
1046 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1048 if (((traits & UpdatePixelTrait) == 0) ||
1049 ((reconstruct_traits & UpdatePixelTrait) == 0))
1051 similarity[j]+=channel_similarity[j];
1053 similarity[CompositePixelChannel]+=
1054 channel_similarity[CompositePixelChannel];
1055 mean_error+=channel_mean_error;
1056 if (channel_maximum_error > maximum_error)
1057 maximum_error=channel_maximum_error;
1060 reconstruct_view=DestroyCacheView(reconstruct_view);
1061 image_view=DestroyCacheView(image_view);
1062 area=MagickSafeReciprocal(area);
1063 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1065 PixelChannel channel = GetPixelChannelChannel(image,k);
1066 PixelTrait traits = GetPixelChannelTraits(image,channel);
1067 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1069 if (((traits & UpdatePixelTrait) == 0) ||
1070 ((reconstruct_traits & UpdatePixelTrait) == 0))
1072 similarity[k]*=area;
1075 similarity[CompositePixelChannel]*=area;
1077 similarity[CompositePixelChannel]/=(double) channels;
1078 image->error.mean_error_per_pixel=QuantumRange*
1079 similarity[CompositePixelChannel];
1080 image->error.normalized_mean_error=mean_error*area;
1081 image->error.normalized_maximum_error=maximum_error;
1085static MagickBooleanType GetMSESimilarity(
const Image *image,
1086 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1096 status = MagickTrue;
1110 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1111 image_view=AcquireVirtualCacheView(image,exception);
1112 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1113#if defined(MAGICKCORE_OPENMP_SUPPORT)
1114 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1115 magick_number_threads(image,image,rows,1)
1117 for (y=0; y < (ssize_t) rows; y++)
1125 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1130 if (status == MagickFalse)
1132 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1133 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1134 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1139 for (x=0; x < (ssize_t) columns; x++)
1148 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1149 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1151 p+=(ptrdiff_t) GetPixelChannels(image);
1152 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1155 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1156 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1157 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1162 PixelChannel channel = GetPixelChannelChannel(image,i);
1163 PixelTrait traits = GetPixelChannelTraits(image,channel);
1164 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1166 if (((traits & UpdatePixelTrait) == 0) ||
1167 ((reconstruct_traits & UpdatePixelTrait) == 0))
1169 if (channel == AlphaPixelChannel)
1170 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1171 reconstruct_image,channel,q));
1173 error=QuantumScale*(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1175 channel_similarity[i]+=error*error;
1176 channel_similarity[CompositePixelChannel]+=error*error;
1179 p+=(ptrdiff_t) GetPixelChannels(image);
1180 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1182#if defined(MAGICKCORE_OPENMP_SUPPORT)
1183 #pragma omp critical (MagickCore_GetMSESimilarity)
1190 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1192 PixelChannel channel = GetPixelChannelChannel(image,j);
1193 PixelTrait traits = GetPixelChannelTraits(image,channel);
1194 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1196 if (((traits & UpdatePixelTrait) == 0) ||
1197 ((reconstruct_traits & UpdatePixelTrait) == 0))
1199 similarity[j]+=channel_similarity[j];
1201 similarity[CompositePixelChannel]+=
1202 channel_similarity[CompositePixelChannel];
1205 reconstruct_view=DestroyCacheView(reconstruct_view);
1206 image_view=DestroyCacheView(image_view);
1207 area=MagickSafeReciprocal(area);
1208 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1210 PixelChannel channel = GetPixelChannelChannel(image,k);
1211 PixelTrait traits = GetPixelChannelTraits(image,channel);
1212 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1214 if (((traits & UpdatePixelTrait) == 0) ||
1215 ((reconstruct_traits & UpdatePixelTrait) == 0))
1217 similarity[k]*=area;
1220 similarity[CompositePixelChannel]*=area;
1222 similarity[CompositePixelChannel]/=(double) channels;
1226static MagickBooleanType GetNCCSimilarity(
const Image *image,
1227 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1235 *reconstruct_statistics;
1238 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1239 variance[MaxPixelChannels+1] = { 0.0 };
1242 status = MagickTrue;
1258 image_statistics=GetImageStatistics(image,exception);
1259 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1260 if ((image_statistics == (ChannelStatistics *) NULL) ||
1261 (reconstruct_statistics == (ChannelStatistics *) NULL))
1263 if (image_statistics != (ChannelStatistics *) NULL)
1264 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1266 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1267 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1268 reconstruct_statistics);
1269 return(MagickFalse);
1271 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1272 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1273 image_view=AcquireVirtualCacheView(image,exception);
1274 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1275#if defined(MAGICKCORE_OPENMP_SUPPORT)
1276 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1277 magick_number_threads(image,image,rows,1)
1279 for (y=0; y < (ssize_t) rows; y++)
1286 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1287 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1288 channel_variance[MaxPixelChannels+1] = { 0.0 };
1293 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1294 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1295 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1300 for (x=0; x < (ssize_t) columns; x++)
1309 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1310 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1312 p+=(ptrdiff_t) GetPixelChannels(image);
1313 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1316 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1317 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1318 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1324 PixelChannel channel = GetPixelChannelChannel(image,i);
1325 PixelTrait traits = GetPixelChannelTraits(image,channel);
1326 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1328 if (((traits & UpdatePixelTrait) == 0) ||
1329 ((reconstruct_traits & UpdatePixelTrait) == 0))
1331 if (channel == AlphaPixelChannel)
1333 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1334 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1335 channel,q)-reconstruct_statistics[channel].mean);
1339 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
1340 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
1341 q)-reconstruct_statistics[channel].mean);
1343 channel_similarity[i]+=alpha*beta;
1344 channel_variance[i]+=alpha*alpha;
1345 channel_reconstruct_variance[i]+=beta*beta;
1347 p+=(ptrdiff_t) GetPixelChannels(image);
1348 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1350#if defined(MAGICKCORE_OPENMP_SUPPORT)
1351 #pragma omp critical (MagickCore_GetNCCSimilarity)
1357 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1359 PixelChannel channel = GetPixelChannelChannel(image,j);
1360 PixelTrait traits = GetPixelChannelTraits(image,channel);
1361 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1363 if (((traits & UpdatePixelTrait) == 0) ||
1364 ((reconstruct_traits & UpdatePixelTrait) == 0))
1366 similarity[j]+=channel_similarity[j];
1367 similarity[CompositePixelChannel]+=channel_similarity[j];
1368 variance[j]+=channel_variance[j];
1369 variance[CompositePixelChannel]+=channel_variance[j];
1370 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1371 reconstruct_variance[CompositePixelChannel]+=
1372 channel_reconstruct_variance[j];
1375 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1380#if defined(MAGICKCORE_OPENMP_SUPPORT)
1384 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1385 if (proceed == MagickFalse)
1392 reconstruct_view=DestroyCacheView(reconstruct_view);
1393 image_view=DestroyCacheView(image_view);
1397 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1399 PixelChannel channel = GetPixelChannelChannel(image,k);
1400 PixelTrait traits = GetPixelChannelTraits(image,channel);
1401 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1403 if (((traits & UpdatePixelTrait) == 0) ||
1404 ((reconstruct_traits & UpdatePixelTrait) == 0))
1406 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1407 sqrt(reconstruct_variance[k]));
1409 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1410 variance[CompositePixelChannel])*sqrt(
1411 reconstruct_variance[CompositePixelChannel]));
1415 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1416 reconstruct_statistics);
1417 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1422static MagickBooleanType GetPASimilarity(
const Image *image,
1423 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1430 status = MagickTrue;
1442 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1443 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1444 image_view=AcquireVirtualCacheView(image,exception);
1445 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1446#if defined(MAGICKCORE_OPENMP_SUPPORT)
1447 #pragma omp parallel for schedule(static) shared(similarity,status) \
1448 magick_number_threads(image,image,rows,1)
1450 for (y=0; y < (ssize_t) rows; y++)
1457 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1462 if (status == MagickFalse)
1464 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1465 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1466 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1471 for (x=0; x < (ssize_t) columns; x++)
1480 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1481 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1483 p+=(ptrdiff_t) GetPixelChannels(image);
1484 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1487 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1488 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1489 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1494 PixelChannel channel = GetPixelChannelChannel(image,i);
1495 PixelTrait traits = GetPixelChannelTraits(image,channel);
1496 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1498 if (((traits & UpdatePixelTrait) == 0) ||
1499 ((reconstruct_traits & UpdatePixelTrait) == 0))
1501 if (channel == AlphaPixelChannel)
1502 distance=QuantumScale*fabs((
double) p[i]-(
double)
1503 GetPixelChannel(reconstruct_image,channel,q));
1505 distance=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(
1506 reconstruct_image,channel,q));
1507 if (distance > channel_similarity[i])
1508 channel_similarity[i]=distance;
1509 if (distance > channel_similarity[CompositePixelChannel])
1510 channel_similarity[CompositePixelChannel]=distance;
1512 p+=(ptrdiff_t) GetPixelChannels(image);
1513 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1515#if defined(MAGICKCORE_OPENMP_SUPPORT)
1516 #pragma omp critical (MagickCore_GetPASimilarity)
1522 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1524 PixelChannel channel = GetPixelChannelChannel(image,j);
1525 PixelTrait traits = GetPixelChannelTraits(image,channel);
1526 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1528 if (((traits & UpdatePixelTrait) == 0) ||
1529 ((reconstruct_traits & UpdatePixelTrait) == 0))
1531 if (channel_similarity[j] > similarity[j])
1532 similarity[j]=channel_similarity[j];
1534 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1535 similarity[CompositePixelChannel]=
1536 channel_similarity[CompositePixelChannel];
1539 reconstruct_view=DestroyCacheView(reconstruct_view);
1540 image_view=DestroyCacheView(image_view);
1544static MagickBooleanType GetPDCSimilarity(
const Image *image,
1545 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1555 status = MagickTrue;
1567 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1568 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1569 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1570 image_view=AcquireVirtualCacheView(image,exception);
1571 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1572#if defined(MAGICKCORE_OPENMP_SUPPORT)
1573 #pragma omp parallel for schedule(static) shared(similarity,status) \
1574 magick_number_threads(image,image,rows,1)
1576 for (y=0; y < (ssize_t) rows; y++)
1583 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1588 if (status == MagickFalse)
1590 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1591 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1592 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1597 for (x=0; x < (ssize_t) columns; x++)
1609 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1610 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1612 p+=(ptrdiff_t) GetPixelChannels(image);
1613 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1616 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1617 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1618 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1623 PixelChannel channel = GetPixelChannelChannel(image,i);
1624 PixelTrait traits = GetPixelChannelTraits(image,channel);
1625 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1627 if (((traits & UpdatePixelTrait) == 0) ||
1628 ((reconstruct_traits & UpdatePixelTrait) == 0))
1630 if (channel == AlphaPixelChannel)
1631 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1634 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1635 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1637 channel_similarity[i]++;
1642 channel_similarity[CompositePixelChannel]++;
1643 p+=(ptrdiff_t) GetPixelChannels(image);
1644 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1646#if defined(MAGICKCORE_OPENMP_SUPPORT)
1647 #pragma omp critical (MagickCore_GetPDCSimilarity)
1653 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1655 PixelChannel channel = GetPixelChannelChannel(image,j);
1656 PixelTrait traits = GetPixelChannelTraits(image,channel);
1657 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1659 if (((traits & UpdatePixelTrait) == 0) ||
1660 ((reconstruct_traits & UpdatePixelTrait) == 0))
1662 similarity[j]+=channel_similarity[j];
1664 similarity[CompositePixelChannel]+=
1665 channel_similarity[CompositePixelChannel];
1668 reconstruct_view=DestroyCacheView(reconstruct_view);
1669 image_view=DestroyCacheView(image_view);
1673static Image *GetPHASECorrelationSurface(
const Image *image,
1674 ExceptionInfo *exception)
1686 kernel=AcquireKernelInfo(
"3x3: 0,-1,0 -1,4,-1 0,-1,0",exception);
1687 if (kernel == (KernelInfo *) NULL)
1688 return((Image *) NULL);
1689 surface=MorphologyImage(image,ConvolveMorphology,1,kernel,exception);
1690 kernel=DestroyKernelInfo(kernel);
1694static MagickBooleanType GetPHASESimilarity(
const Image *image,
1695 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1703 correlation[MaxPixelChannels+1] = { 0.0 },
1704 image_sum[MaxPixelChannels+1] = { 0.0 },
1705 image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1706 reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1707 reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1714 status = MagickTrue;
1729 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1730 phase_image=GetPHASECorrelationSurface(image,exception);
1731 phase_reconstruct=GetPHASECorrelationSurface(reconstruct_image,exception);
1732 if ((phase_image == (Image *) NULL) ||
1733 (phase_reconstruct == (Image *) NULL))
1735 if (phase_image != (Image *) NULL)
1736 phase_image=DestroyImage(phase_image);
1737 if (phase_reconstruct != (Image *) NULL)
1738 phase_reconstruct=DestroyImage(phase_reconstruct);
1739 return(MagickFalse);
1741 image_view=AcquireVirtualCacheView(phase_image,exception);
1742 reconstruct_view=AcquireVirtualCacheView(phase_reconstruct,exception);
1743#if defined(MAGICKCORE_OPENMP_SUPPORT)
1744 #pragma omp parallel for schedule(static) shared(status) \
1745 magick_number_threads(phase_image,phase_reconstruct,rows,1)
1747 for (y=0; y < (ssize_t) rows; y++)
1755 channel_correlation[MaxPixelChannels+1] = { 0.0 },
1756 channel_image_sum[MaxPixelChannels+1] = { 0.0 },
1757 channel_image_sum_squared[MaxPixelChannels+1] = { 0.0 },
1758 channel_reconstruct_sum[MaxPixelChannels+1] = { 0.0 },
1759 channel_reconstruct_sum_squared[MaxPixelChannels+1] = { 0.0 };
1765 if (status == MagickFalse)
1767 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1768 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1769 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1774 for (x=0; x < (ssize_t) columns; x++)
1776 if ((GetPixelReadMask(phase_image,p) <= (QuantumRange/2)) ||
1777 (GetPixelReadMask(phase_reconstruct,q) <= (QuantumRange/2)))
1779 p+=(ptrdiff_t) GetPixelChannels(phase_image);
1780 q+=(ptrdiff_t) GetPixelChannels(phase_reconstruct);
1783 for (i=0; i < (ssize_t) GetPixelChannels(phase_image); i++)
1799 channel=GetPixelChannelChannel(phase_image,i);
1800 traits=GetPixelChannelTraits(phase_image,channel);
1801 reconstruct_traits=GetPixelChannelTraits(phase_reconstruct,channel);
1802 if (((traits & UpdatePixelTrait) == 0) ||
1803 ((reconstruct_traits & UpdatePixelTrait) == 0))
1805 offset=GetPixelChannelOffset(phase_reconstruct,channel);
1808 alpha=QuantumScale*(double) p[i];
1809 beta=QuantumScale*(double) q[offset];
1810 channel_image_sum[i]+=alpha;
1811 channel_image_sum_squared[i]+=alpha*alpha;
1812 channel_reconstruct_sum[i]+=beta;
1813 channel_reconstruct_sum_squared[i]+=beta*beta;
1814 channel_correlation[i]+=alpha*beta;
1817 p+=(ptrdiff_t) GetPixelChannels(phase_image);
1818 q+=(ptrdiff_t) GetPixelChannels(phase_reconstruct);
1820#if defined(MAGICKCORE_OPENMP_SUPPORT)
1821 #pragma omp critical (MagickCore_GetPHASESimilarity)
1825 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1827 correlation[i]+=channel_correlation[i];
1828 image_sum[i]+=channel_image_sum[i];
1829 image_sum_squared[i]+=channel_image_sum_squared[i];
1830 reconstruct_sum[i]+=channel_reconstruct_sum[i];
1831 reconstruct_sum_squared[i]+=channel_reconstruct_sum_squared[i];
1835 reconstruct_view=DestroyCacheView(reconstruct_view);
1836 image_view=DestroyCacheView(image_view);
1837 phase_image=DestroyImage(phase_image);
1838 phase_reconstruct=DestroyImage(phase_reconstruct);
1839 if ((status == MagickFalse) || (area < 1.0))
1845 similarity[CompositePixelChannel]=0.0;
1846 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1860 channel=GetPixelChannelChannel(image,j);
1861 traits=GetPixelChannelTraits(image,channel);
1862 reconstruct_traits=GetPixelChannelTraits(reconstruct_image,channel);
1863 if (((traits & UpdatePixelTrait) == 0) ||
1864 ((reconstruct_traits & UpdatePixelTrait) == 0))
1866 numerator=area*correlation[j]-image_sum[j]*reconstruct_sum[j];
1867 denominator=sqrt(area*image_sum_squared[j]-image_sum[j]*image_sum[j])*
1868 sqrt(area*reconstruct_sum_squared[j]-reconstruct_sum[j]*
1869 reconstruct_sum[j]);
1870 pearson=denominator < MagickEpsilon ? 0.0 : numerator/denominator;
1875 similarity[j]=pearson;
1876 similarity[CompositePixelChannel]+=pearson;
1880 similarity[CompositePixelChannel]/=(double) channels;
1884static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1885 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1887 ChannelPerceptualHash
1901 channel_phash=GetImagePerceptualHash(image,exception);
1902 if (channel_phash == (ChannelPerceptualHash *) NULL)
1903 return(MagickFalse);
1904 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1905 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1907 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1909 return(MagickFalse);
1911 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1919 PixelChannel channel = GetPixelChannelChannel(image,i);
1920 PixelTrait traits = GetPixelChannelTraits(image,channel);
1921 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1923 if (((traits & UpdatePixelTrait) == 0) ||
1924 ((reconstruct_traits & UpdatePixelTrait) == 0))
1926 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1935 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1940 alpha=channel_phash[i].phash[j][k];
1941 beta=reconstruct_phash[i].phash[j][k];
1943 if (IsNaN(error) != 0)
1945 difference+=error*error;
1948 similarity[i]+=difference;
1949 similarity[CompositePixelChannel]+=difference;
1953 similarity[CompositePixelChannel]/=(double) channels;
1954 artifact=GetImageArtifact(image,
"phash:normalize");
1955 if (IsStringTrue(artifact) != MagickFalse)
1957 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1959 PixelChannel channel = GetPixelChannelChannel(image,i);
1960 PixelTrait traits = GetPixelChannelTraits(image,channel);
1961 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1963 if (((traits & UpdatePixelTrait) == 0) ||
1964 ((reconstruct_traits & UpdatePixelTrait) == 0))
1966 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
1968 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
1969 channel_phash[0].number_colorspaces);
1974 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1976 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
1980static MagickBooleanType GetPSNRSimilarity(
const Image *image,
1981 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1984 status = MagickTrue;
1992 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
1993 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1995 PixelChannel channel = GetPixelChannelChannel(image,i);
1996 PixelTrait traits = GetPixelChannelTraits(image,channel);
1997 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1999 if (((traits & UpdatePixelTrait) == 0) ||
2000 ((reconstruct_traits & UpdatePixelTrait) == 0))
2002 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
2003 similarity[i]))/MagickSafePSNRRecipicol(10.0);
2005 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
2006 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
2007 MagickSafePSNRRecipicol(10.0);
2011static MagickBooleanType GetRMSESimilarity(
const Image *image,
2012 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2014#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2017 status = MagickTrue;
2025 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2026 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2028 PixelChannel channel = GetPixelChannelChannel(image,i);
2029 PixelTrait traits = GetPixelChannelTraits(image,channel);
2030 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2032 if (((traits & UpdatePixelTrait) == 0) ||
2033 ((reconstruct_traits & UpdatePixelTrait) == 0))
2035 similarity[i]=RMSESquareRoot(similarity[i]);
2037 similarity[CompositePixelChannel]=RMSESquareRoot(
2038 similarity[CompositePixelChannel]);
2042static MagickBooleanType GetSSIMSimularity(
const Image *image,
2043 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2045#define SSIMRadius 5.0
2046#define SSIMSigma 1.5
2056 geometry[MagickPathExtent];
2072 status = MagickTrue;
2087 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2088 if (artifact != (
const char *) NULL)
2089 radius=StringToDouble(artifact,(
char **) NULL);
2091 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2092 if (artifact != (
const char *) NULL)
2093 sigma=StringToDouble(artifact,(
char **) NULL);
2094 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.17gx%.17g",
2096 kernel_info=AcquireKernelInfo(geometry,exception);
2097 if (kernel_info == (KernelInfo *) NULL)
2098 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2100 c1=pow(SSIMK1*SSIML,2.0);
2101 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2102 if (artifact != (
const char *) NULL)
2103 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2104 c2=pow(SSIMK2*SSIML,2.0);
2105 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2106 if (artifact != (
const char *) NULL)
2107 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2108 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2109 image_view=AcquireVirtualCacheView(image,exception);
2110 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2111#if defined(MAGICKCORE_OPENMP_SUPPORT)
2112 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2113 magick_number_threads(image,reconstruct_image,rows,1)
2115 for (y=0; y < (ssize_t) rows; y++)
2123 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2129 if (status == MagickFalse)
2131 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2132 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2133 kernel_info->height,exception);
2134 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2135 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2136 kernel_info->height,exception);
2137 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2142 for (x=0; x < (ssize_t) columns; x++)
2145 *magick_restrict reconstruct,
2146 *magick_restrict test;
2149 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2150 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2151 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2152 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2153 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2161 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2162 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2164 p+=(ptrdiff_t) GetPixelChannels(image);
2165 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2168 k=kernel_info->values;
2171 for (v=0; v < (ssize_t) kernel_info->height; v++)
2176 for (u=0; u < (ssize_t) kernel_info->width; u++)
2178 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2184 PixelChannel channel = GetPixelChannelChannel(image,i);
2185 PixelTrait traits = GetPixelChannelTraits(image,channel);
2186 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2187 reconstruct_image,channel);
2188 if (((traits & UpdatePixelTrait) == 0) ||
2189 ((reconstruct_traits & UpdatePixelTrait) == 0))
2191 x_pixel=QuantumScale*(double) test[i];
2192 x_pixel_mu[i]+=(*k)*x_pixel;
2193 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2194 y_pixel=QuantumScale*(double)
2195 GetPixelChannel(reconstruct_image,channel,reconstruct);
2196 y_pixel_mu[i]+=(*k)*y_pixel;
2197 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2198 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2201 test+=(ptrdiff_t) GetPixelChannels(image);
2202 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2204 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2205 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2207 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2212 x_pixel_sigmas_squared,
2216 y_pixel_sigmas_squared;
2218 PixelChannel channel = GetPixelChannelChannel(image,i);
2219 PixelTrait traits = GetPixelChannelTraits(image,channel);
2220 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2221 reconstruct_image,channel);
2222 if (((traits & UpdatePixelTrait) == 0) ||
2223 ((reconstruct_traits & UpdatePixelTrait) == 0))
2225 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2226 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2227 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2228 xy_sigmas=xy_sigma[i]-xy_mu;
2229 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2230 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2231 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2232 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2233 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2234 channel_similarity[i]+=ssim;
2235 channel_similarity[CompositePixelChannel]+=ssim;
2237 p+=(ptrdiff_t) GetPixelChannels(image);
2238 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2241#if defined(MAGICKCORE_OPENMP_SUPPORT)
2242 #pragma omp critical (MagickCore_GetSSIMSimularity)
2249 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2251 PixelChannel channel = GetPixelChannelChannel(image,j);
2252 PixelTrait traits = GetPixelChannelTraits(image,channel);
2253 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2255 if (((traits & UpdatePixelTrait) == 0) ||
2256 ((reconstruct_traits & UpdatePixelTrait) == 0))
2258 similarity[j]+=channel_similarity[j];
2260 similarity[CompositePixelChannel]+=
2261 channel_similarity[CompositePixelChannel];
2264 image_view=DestroyCacheView(image_view);
2265 reconstruct_view=DestroyCacheView(reconstruct_view);
2266 kernel_info=DestroyKernelInfo(kernel_info);
2267 area=MagickSafeReciprocal(area);
2268 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2270 PixelChannel channel = GetPixelChannelChannel(image,l);
2271 PixelTrait traits = GetPixelChannelTraits(image,channel);
2272 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2274 if (((traits & UpdatePixelTrait) == 0) ||
2275 ((reconstruct_traits & UpdatePixelTrait) == 0))
2277 similarity[l]*=area;
2280 similarity[CompositePixelChannel]*=area;
2282 similarity[CompositePixelChannel]/=(double) channels;
2286static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2287 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2290 status = MagickTrue;
2298 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2299 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2301 PixelChannel channel = GetPixelChannelChannel(image,i);
2302 PixelTrait traits = GetPixelChannelTraits(image,channel);
2303 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2305 if (((traits & UpdatePixelTrait) == 0) ||
2306 ((reconstruct_traits & UpdatePixelTrait) == 0))
2308 similarity[i]=(1.0-similarity[i])/2.0;
2310 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2314MagickExport MagickBooleanType GetImageDistortion(Image *image,
2315 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2316 ExceptionInfo *exception)
2318#define CompareMetricNotSupportedException "metric not supported"
2321 *channel_similarity;
2324 status = MagickTrue;
2329 assert(image != (Image *) NULL);
2330 assert(image->signature == MagickCoreSignature);
2331 assert(reconstruct_image != (
const Image *) NULL);
2332 assert(reconstruct_image->signature == MagickCoreSignature);
2333 assert(distortion != (
double *) NULL);
2334 if (IsEventLogging() != MagickFalse)
2335 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2340 length=MaxPixelChannels+1UL;
2341 channel_similarity=(
double *) AcquireQuantumMemory(length,
2342 sizeof(*channel_similarity));
2343 if (channel_similarity == (
double *) NULL)
2344 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2345 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2348 case AbsoluteErrorMetric:
2350 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2354 case DotProductCorrelationErrorMetric:
2356 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2360 case FuzzErrorMetric:
2362 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2366 case MeanAbsoluteErrorMetric:
2368 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2372 case MeanErrorPerPixelErrorMetric:
2374 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2378 case MeanSquaredErrorMetric:
2380 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2384 case NormalizedCrossCorrelationErrorMetric:
2386 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2390 case PeakAbsoluteErrorMetric:
2392 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2396 case PeakSignalToNoiseRatioErrorMetric:
2398 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2402 case PerceptualHashErrorMetric:
2404 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2408 case PhaseCorrelationErrorMetric:
2410 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2414 case PixelDifferenceCountErrorMetric:
2416 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2420 case RootMeanSquaredErrorMetric:
2421 case UndefinedErrorMetric:
2424 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2428 case StructuralDissimilarityErrorMetric:
2430 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2434 case StructuralSimilarityErrorMetric:
2436 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2441 *distortion=channel_similarity[CompositePixelChannel];
2444 case DotProductCorrelationErrorMetric:
2445 case NormalizedCrossCorrelationErrorMetric:
2446 case PhaseCorrelationErrorMetric:
2447 case StructuralSimilarityErrorMetric:
2449 *distortion=(1.0-(*distortion))/2.0;
2454 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2455 if (fabs(*distortion) < MagickEpsilon)
2457 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2493MagickExport
double *GetImageDistortions(Image *image,
2494 const Image *reconstruct_image,
const MetricType metric,
2495 ExceptionInfo *exception)
2499 *channel_similarity;
2502 status = MagickTrue;
2510 assert(image != (Image *) NULL);
2511 assert(image->signature == MagickCoreSignature);
2512 assert(reconstruct_image != (
const Image *) NULL);
2513 assert(reconstruct_image->signature == MagickCoreSignature);
2514 if (IsEventLogging() != MagickFalse)
2515 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2519 length=MaxPixelChannels+1UL;
2520 channel_similarity=(
double *) AcquireQuantumMemory(length,
2521 sizeof(*channel_similarity));
2522 if (channel_similarity == (
double *) NULL)
2523 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2524 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2527 case AbsoluteErrorMetric:
2529 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2533 case DotProductCorrelationErrorMetric:
2535 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2539 case FuzzErrorMetric:
2541 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2545 case MeanAbsoluteErrorMetric:
2547 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2551 case MeanErrorPerPixelErrorMetric:
2553 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2557 case MeanSquaredErrorMetric:
2559 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2563 case NormalizedCrossCorrelationErrorMetric:
2565 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2569 case PeakAbsoluteErrorMetric:
2571 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2575 case PeakSignalToNoiseRatioErrorMetric:
2577 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2581 case PerceptualHashErrorMetric:
2583 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2587 case PhaseCorrelationErrorMetric:
2589 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2593 case PixelDifferenceCountErrorMetric:
2595 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2599 case RootMeanSquaredErrorMetric:
2600 case UndefinedErrorMetric:
2603 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2607 case StructuralDissimilarityErrorMetric:
2609 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2613 case StructuralSimilarityErrorMetric:
2615 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2620 if (status == MagickFalse)
2622 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2623 return((
double *) NULL);
2625 distortion=channel_similarity;
2628 case DotProductCorrelationErrorMetric:
2629 case NormalizedCrossCorrelationErrorMetric:
2630 case PhaseCorrelationErrorMetric:
2631 case StructuralSimilarityErrorMetric:
2633 for (i=0; i <= MaxPixelChannels; i++)
2634 distortion[i]=(1.0-distortion[i])/2.0;
2639 for (i=0; i <= MaxPixelChannels; i++)
2640 if (fabs(distortion[i]) < MagickEpsilon)
2642 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2643 distortion[CompositePixelChannel]);
2675MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2676 const Image *reconstruct_image,ExceptionInfo *exception)
2689 assert(image != (Image *) NULL);
2690 assert(image->signature == MagickCoreSignature);
2691 assert(reconstruct_image != (
const Image *) NULL);
2692 assert(reconstruct_image->signature == MagickCoreSignature);
2693 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2694 image_view=AcquireVirtualCacheView(image,exception);
2695 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2696 for (y=0; y < (ssize_t) rows; y++)
2705 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2706 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2707 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2709 for (x=0; x < (ssize_t) columns; x++)
2714 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2719 PixelChannel channel = GetPixelChannelChannel(image,i);
2720 PixelTrait traits = GetPixelChannelTraits(image,channel);
2721 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2723 if (((traits & UpdatePixelTrait) == 0) ||
2724 ((reconstruct_traits & UpdatePixelTrait) == 0))
2726 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2728 if (distance >= MagickEpsilon)
2731 if (i < (ssize_t) GetPixelChannels(image))
2733 p+=(ptrdiff_t) GetPixelChannels(image);
2734 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2736 if (x < (ssize_t) columns)
2739 reconstruct_view=DestroyCacheView(reconstruct_view);
2740 image_view=DestroyCacheView(image_view);
2741 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2793MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2794 const Image *reconstruct_image,ExceptionInfo *exception)
2797 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2802 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2804 if (status == MagickFalse)
2805 return(MagickFalse);
2806 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2807 MagickTrue : MagickFalse;
2854#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2855static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2856 const Image *beta_image,ExceptionInfo *exception)
2859 *alpha_fft = (Image *) NULL,
2860 *beta_fft = (Image *) NULL,
2861 *complex_conjugate = (Image *) NULL,
2862 *complex_multiplication = (Image *) NULL,
2863 *cross_correlation = (Image *) NULL,
2864 *temp_image = (Image *) NULL;
2869 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2870 if (temp_image == (Image *) NULL)
2871 return((Image *) NULL);
2872 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2873 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2874 temp_image=DestroyImageList(temp_image);
2875 if (beta_fft == (Image *) NULL)
2876 return((Image *) NULL);
2880 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2881 beta_fft=DestroyImageList(beta_fft);
2882 if (complex_conjugate == (Image *) NULL)
2883 return((Image *) NULL);
2887 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2888 if (temp_image == (Image *) NULL)
2890 complex_conjugate=DestroyImageList(complex_conjugate);
2891 return((Image *) NULL);
2893 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2894 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2895 temp_image=DestroyImageList(temp_image);
2896 if (alpha_fft == (Image *) NULL)
2898 complex_conjugate=DestroyImageList(complex_conjugate);
2899 return((Image *) NULL);
2904 DisableCompositeClampUnlessSpecified(complex_conjugate);
2905 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2906 AppendImageToList(&complex_conjugate,alpha_fft);
2907 complex_multiplication=ComplexImages(complex_conjugate,
2908 MultiplyComplexOperator,exception);
2909 complex_conjugate=DestroyImageList(complex_conjugate);
2910 if (complex_multiplication == (Image *) NULL)
2911 return((Image *) NULL);
2915 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2916 complex_multiplication->next,MagickFalse,exception);
2917 complex_multiplication=DestroyImageList(complex_multiplication);
2918 return(cross_correlation);
2921static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2922 ExceptionInfo *exception)
2930 kernel_info=AcquireKernelInfo(kernel,exception);
2931 if (kernel_info == (KernelInfo *) NULL)
2932 return((Image *) NULL);
2933 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2935 kernel_info=DestroyKernelInfo(kernel_info);
2936 return(derivative_image);
2939static Image *SIMDivideImage(
const Image *numerator_image,
2940 const Image *denominator_image,ExceptionInfo *exception)
2950 status = MagickTrue;
2958 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2959 if (divide_image == (Image *) NULL)
2960 return(divide_image);
2961 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2962 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2963#if defined(MAGICKCORE_OPENMP_SUPPORT)
2964 #pragma omp parallel for schedule(static) shared(status) \
2965 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
2967 for (y=0; y < (ssize_t) divide_image->rows; y++)
2978 if (status == MagickFalse)
2980 p=GetCacheViewVirtualPixels(denominator_view,0,y,
2981 denominator_image->columns,1,exception);
2982 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
2984 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2989 for (x=0; x < (ssize_t) divide_image->columns; x++)
2994 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
2996 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
2997 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
2998 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
3000 if (((traits & UpdatePixelTrait) == 0) ||
3001 ((denominator_traits & UpdatePixelTrait) == 0))
3003 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
3004 (
double) GetPixelChannel(denominator_image,channel,p)));
3006 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
3007 q+=(ptrdiff_t) GetPixelChannels(divide_image);
3009 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
3012 denominator_view=DestroyCacheView(denominator_view);
3013 numerator_view=DestroyCacheView(numerator_view);
3014 if (status == MagickFalse)
3015 divide_image=DestroyImage(divide_image);
3016 return(divide_image);
3019static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3020 const Image *source_image,ExceptionInfo *exception)
3029 divide_image=SIMDivideImage(image,magnitude_image,exception);
3030 if (divide_image == (Image *) NULL)
3031 return((Image *) NULL);
3032 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3033 ÷_image->background_color);
3034 SetGeometry(source_image,&geometry);
3035 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3036 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3037 result_image=ExtentImage(divide_image,&geometry,exception);
3038 divide_image=DestroyImage(divide_image);
3039 return(result_image);
3042static MagickBooleanType SIMFilterImageNaNs(Image *image,
3043 ExceptionInfo *exception)
3049 status = MagickTrue;
3057 image_view=AcquireAuthenticCacheView(image,exception);
3058#if defined(MAGICKCORE_OPENMP_SUPPORT)
3059 #pragma omp parallel for schedule(static) shared(status) \
3060 magick_number_threads(image,image,image->rows,1)
3062 for (y=0; y < (ssize_t) image->rows; y++)
3070 if (status == MagickFalse)
3072 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3073 if (q == (Quantum *) NULL)
3078 for (x=0; x < (ssize_t) image->columns; x++)
3083 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3085 PixelChannel channel = GetPixelChannelChannel(image,i);
3086 PixelTrait traits = GetPixelChannelTraits(image,channel);
3087 if ((traits & UpdatePixelTrait) == 0)
3089 if (IsNaN((
double) q[i]) != 0)
3092 q+=(ptrdiff_t) GetPixelChannels(image);
3094 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3097 image_view=DestroyCacheView(image_view);
3101static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3110 status = MagickTrue;
3118 square_image=CloneImage(image,0,0,MagickTrue,exception);
3119 if (square_image == (Image *) NULL)
3120 return(square_image);
3121 image_view=AcquireAuthenticCacheView(square_image,exception);
3122#if defined(MAGICKCORE_OPENMP_SUPPORT)
3123 #pragma omp parallel for schedule(static) shared(status) \
3124 magick_number_threads(square_image,square_image,square_image->rows,1)
3126 for (y=0; y < (ssize_t) square_image->rows; y++)
3134 if (status == MagickFalse)
3136 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3138 if (q == (Quantum *) NULL)
3143 for (x=0; x < (ssize_t) square_image->columns; x++)
3148 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3150 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3151 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3152 if ((traits & UpdatePixelTrait) == 0)
3154 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3156 q+=(ptrdiff_t) GetPixelChannels(square_image);
3158 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3161 image_view=DestroyCacheView(image_view);
3162 if (status == MagickFalse)
3163 square_image=DestroyImage(square_image);
3164 return(square_image);
3167static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3168 ExceptionInfo *exception)
3176 status = MagickTrue;
3178 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3179 xsq_image=SIMSquareImage(alpha_image,exception);
3180 if (xsq_image == (Image *) NULL)
3181 return((Image *) NULL);
3182 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3183 ysq_image=SIMSquareImage(beta_image,exception);
3184 if (ysq_image == (Image *) NULL)
3186 xsq_image=DestroyImage(xsq_image);
3187 return((Image *) NULL);
3189 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3191 magnitude_image=xsq_image;
3192 ysq_image=DestroyImage(ysq_image);
3193 if (status == MagickFalse)
3195 magnitude_image=DestroyImage(magnitude_image);
3196 return((Image *) NULL);
3198 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3199 if (status == MagickFalse)
3201 magnitude_image=DestroyImage(magnitude_image);
3202 return (Image *) NULL;
3204 return(magnitude_image);
3207static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3208 RectangleInfo *offset,ExceptionInfo *exception)
3227 status = MagickTrue;
3230 maxima_info = { -MagickMaximumValue, 0, 0 };
3238 image_view=AcquireVirtualCacheView(image,exception);
3239 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3241 if (q != (
const Quantum *) NULL)
3242 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3243#if defined(MAGICKCORE_OPENMP_SUPPORT)
3244 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3245 magick_number_threads(image,image,image->rows,1)
3247 for (y=0; y < (ssize_t) image->rows; y++)
3253 channel_maxima = { -MagickMaximumValue, 0, 0 };
3258 if (status == MagickFalse)
3260 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3261 if (p == (
const Quantum *) NULL)
3266 channel_maxima=maxima_info;
3267 for (x=0; x < (ssize_t) image->columns; x++)
3272 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3277 PixelChannel channel = GetPixelChannelChannel(image,i);
3278 PixelTrait traits = GetPixelChannelTraits(image,channel);
3279 if ((traits & UpdatePixelTrait) == 0)
3281 pixel=(double) p[i];
3282 if (IsNaN(pixel) != 0)
3284 if (pixel > channel_maxima.maxima)
3286 channel_maxima.maxima=(double) p[i];
3291 p+=(ptrdiff_t) GetPixelChannels(image);
3293#if defined(MAGICKCORE_OPENMP_SUPPORT)
3294 #pragma omp critical (MagickCore_SIMMaximaImage)
3296 if (channel_maxima.maxima > maxima_info.maxima)
3297 maxima_info=channel_maxima;
3299 image_view=DestroyCacheView(image_view);
3300 *maxima=maxima_info.maxima;
3301 offset->x=maxima_info.x;
3302 offset->y=maxima_info.y;
3306static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3307 RectangleInfo *offset,ExceptionInfo *exception)
3326 status = MagickTrue;
3329 minima_info = { MagickMaximumValue, 0, 0 };
3337 image_view=AcquireVirtualCacheView(image,exception);
3338 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3340 if (q != (
const Quantum *) NULL)
3341 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3342#if defined(MAGICKCORE_OPENMP_SUPPORT)
3343 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3344 magick_number_threads(image,image,image->rows,1)
3346 for (y=0; y < (ssize_t) image->rows; y++)
3352 channel_minima = { MagickMaximumValue, 0, 0 };
3357 if (status == MagickFalse)
3359 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3360 if (p == (
const Quantum *) NULL)
3365 channel_minima=minima_info;
3366 for (x=0; x < (ssize_t) image->columns; x++)
3371 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3376 PixelChannel channel = GetPixelChannelChannel(image,i);
3377 PixelTrait traits = GetPixelChannelTraits(image,channel);
3378 if ((traits & UpdatePixelTrait) == 0)
3380 pixel=(double) p[i];
3381 if (IsNaN(pixel) != 0)
3383 if (pixel < channel_minima.minima)
3385 channel_minima.minima=pixel;
3390 p+=(ptrdiff_t) GetPixelChannels(image);
3392#if defined(MAGICKCORE_OPENMP_SUPPORT)
3393 #pragma omp critical (MagickCore_SIMMinimaImage)
3395 if (channel_minima.minima < minima_info.minima)
3396 minima_info=channel_minima;
3398 image_view=DestroyCacheView(image_view);
3399 *minima=minima_info.minima;
3400 offset->x=minima_info.x;
3401 offset->y=minima_info.y;
3405static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3406 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3412 status = MagickTrue;
3420 image_view=AcquireAuthenticCacheView(image,exception);
3421#if defined(MAGICKCORE_OPENMP_SUPPORT)
3422 #pragma omp parallel for schedule(static) shared(status) \
3423 magick_number_threads(image,image,image->rows,1)
3425 for (y=0; y < (ssize_t) image->rows; y++)
3433 if (status == MagickFalse)
3435 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3436 if (q == (Quantum *) NULL)
3441 for (x=0; x < (ssize_t) image->columns; x++)
3446 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3448 PixelChannel channel = GetPixelChannelChannel(image,i);
3449 PixelTrait traits = GetPixelChannelTraits(image,channel);
3450 if ((traits & UpdatePixelTrait) == 0)
3452 if (channel_statistics != (
const ChannelStatistics *) NULL)
3453 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3454 channel_statistics[channel].standard_deviation);
3456 q[i]=(Quantum) (factor*q[i]);
3458 q+=(ptrdiff_t) GetPixelChannels(image);
3460 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3463 image_view=DestroyCacheView(image_view);
3467static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3468 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3471 *alpha_fft = (Image *) NULL,
3472 *beta_fft = (Image *) NULL,
3473 *complex_multiplication = (Image *) NULL,
3474 *cross_correlation = (Image *) NULL;
3479 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3480 if (beta_fft == NULL)
3481 return((Image *) NULL);
3482 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3483 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3484 if (beta_fft == NULL)
3485 return((Image *) NULL);
3489 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3490 if (alpha_fft == (Image *) NULL)
3492 beta_fft=DestroyImageList(beta_fft);
3493 return((Image *) NULL);
3495 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3496 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3497 if (alpha_fft == (Image *) NULL)
3499 beta_fft=DestroyImageList(beta_fft);
3500 return((Image *) NULL);
3505 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3506 if (beta_fft == (Image *) NULL)
3508 alpha_fft=DestroyImageList(alpha_fft);
3509 return((Image *) NULL);
3514 AppendImageToList(&beta_fft,alpha_fft);
3515 DisableCompositeClampUnlessSpecified(beta_fft);
3516 DisableCompositeClampUnlessSpecified(beta_fft->next);
3517 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3519 beta_fft=DestroyImageList(beta_fft);
3520 if (complex_multiplication == (Image *) NULL)
3521 return((Image *) NULL);
3525 CompositeLayers((Image *) magnitude_image,DivideDstCompositeOp,
3526 complex_multiplication,0,0,exception);
3530 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3531 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3532 complex_multiplication->next,MagickFalse,exception);
3533 complex_multiplication=DestroyImageList(complex_multiplication);
3534 return(cross_correlation);
3537static MagickBooleanType SIMSetImageMean(Image *image,
3538 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3544 status = MagickTrue;
3552 image_view=AcquireAuthenticCacheView(image,exception);
3553#if defined(MAGICKCORE_OPENMP_SUPPORT)
3554 #pragma omp parallel for schedule(static) shared(status) \
3555 magick_number_threads(image,image,image->rows,1)
3557 for (y=0; y < (ssize_t) image->rows; y++)
3565 if (status == MagickFalse)
3567 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3568 if (q == (Quantum *) NULL)
3573 for (x=0; x < (ssize_t) image->columns; x++)
3578 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3580 PixelChannel channel = GetPixelChannelChannel(image,i);
3581 PixelTrait traits = GetPixelChannelTraits(image,channel);
3582 if ((traits & UpdatePixelTrait) == 0)
3584 q[i]=(Quantum) channel_statistics[channel].mean;
3586 q+=(ptrdiff_t) GetPixelChannels(image);
3588 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3591 image_view=DestroyCacheView(image_view);
3595static Image *SIMSubtractImageMean(
const Image *alpha_image,
3596 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3597 ExceptionInfo *exception)
3607 status = MagickTrue;
3615 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3616 MagickTrue,exception);
3617 if (subtract_image == (Image *) NULL)
3618 return(subtract_image);
3619 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3620 beta_view=AcquireVirtualCacheView(beta_image,exception);
3621#if defined(MAGICKCORE_OPENMP_SUPPORT)
3622 #pragma omp parallel for schedule(static) shared(status) \
3623 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3625 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3636 if (status == MagickFalse)
3638 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3639 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3641 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3646 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3651 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3653 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3654 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3655 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3656 if (((traits & UpdatePixelTrait) == 0) ||
3657 ((beta_traits & UpdatePixelTrait) == 0))
3659 if ((x >= (ssize_t) beta_image->columns) ||
3660 (y >= (ssize_t) beta_image->rows))
3663 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3664 channel_statistics[channel].mean);
3666 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3667 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3669 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3672 beta_view=DestroyCacheView(beta_view);
3673 image_view=DestroyCacheView(image_view);
3674 if (status == MagickFalse)
3675 subtract_image=DestroyImage(subtract_image);
3676 return(subtract_image);
3679static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3680 ExceptionInfo *exception)
3689 status = MagickTrue;
3697 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3698 MagickTrue,exception);
3699 if (unity_image == (Image *) NULL)
3700 return(unity_image);
3701 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3702 return(DestroyImage(unity_image));
3703 image_view=AcquireAuthenticCacheView(unity_image,exception);
3704#if defined(MAGICKCORE_OPENMP_SUPPORT)
3705 #pragma omp parallel for schedule(static) shared(status) \
3706 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3708 for (y=0; y < (ssize_t) unity_image->rows; y++)
3716 if (status == MagickFalse)
3718 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3720 if (q == (Quantum *) NULL)
3725 for (x=0; x < (ssize_t) unity_image->columns; x++)
3730 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3732 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3733 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3734 if ((traits & UpdatePixelTrait) == 0)
3736 if ((x >= (ssize_t) beta_image->columns) ||
3737 (y >= (ssize_t) beta_image->rows))
3742 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3744 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3747 image_view=DestroyCacheView(image_view);
3748 if (status == MagickFalse)
3749 unity_image=DestroyImage(unity_image);
3750 return(unity_image);
3753static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3754 ExceptionInfo *exception)
3764 status = MagickTrue;
3772 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3773 if (variance_image == (Image *) NULL)
3774 return(variance_image);
3775 image_view=AcquireAuthenticCacheView(variance_image,exception);
3776 beta_view=AcquireVirtualCacheView(beta_image,exception);
3777#if defined(MAGICKCORE_OPENMP_SUPPORT)
3778 #pragma omp parallel for schedule(static) shared(status) \
3779 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3781 for (y=0; y < (ssize_t) variance_image->rows; y++)
3792 if (status == MagickFalse)
3794 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3796 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3798 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3803 for (x=0; x < (ssize_t) variance_image->columns; x++)
3808 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3813 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3814 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3815 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3816 if (((traits & UpdatePixelTrait) == 0) ||
3817 ((beta_traits & UpdatePixelTrait) == 0))
3819 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3820 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3821 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3823 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3824 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3826 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3829 beta_view=DestroyCacheView(beta_view);
3830 image_view=DestroyCacheView(image_view);
3831 if (status == MagickFalse)
3832 variance_image=DestroyImage(variance_image);
3833 return(variance_image);
3836static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3837 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3839#define ThrowDPCSimilarityException() \
3841 if (dot_product_image != (Image *) NULL) \
3842 dot_product_image=DestroyImage(dot_product_image); \
3843 if (magnitude_image != (Image *) NULL) \
3844 magnitude_image=DestroyImage(magnitude_image); \
3845 if (reconstruct_image != (Image *) NULL) \
3846 reconstruct_image=DestroyImage(reconstruct_image); \
3847 if (rx_image != (Image *) NULL) \
3848 rx_image=DestroyImage(rx_image); \
3849 if (ry_image != (Image *) NULL) \
3850 ry_image=DestroyImage(ry_image); \
3851 if (target_image != (Image *) NULL) \
3852 target_image=DestroyImage(target_image); \
3853 if (threshold_image != (Image *) NULL) \
3854 threshold_image=DestroyImage(threshold_image); \
3855 if (trx_image != (Image *) NULL) \
3856 trx_image=DestroyImage(trx_image); \
3857 if (try_image != (Image *) NULL) \
3858 try_image=DestroyImage(try_image); \
3859 if (tx_image != (Image *) NULL) \
3860 tx_image=DestroyImage(tx_image); \
3861 if (ty_image != (Image *) NULL) \
3862 ty_image=DestroyImage(ty_image); \
3863 return((Image *) NULL); \
3870 standard_deviation = 0.0;
3873 *dot_product_image = (Image *) NULL,
3874 *magnitude_image = (Image *) NULL,
3875 *reconstruct_image = (Image *) NULL,
3876 *rx_image = (Image *) NULL,
3877 *ry_image = (Image *) NULL,
3878 *trx_image = (Image *) NULL,
3879 *target_image = (Image *) NULL,
3880 *threshold_image = (Image *) NULL,
3881 *try_image = (Image *) NULL,
3882 *tx_image = (Image *) NULL,
3883 *ty_image = (Image *) NULL;
3886 status = MagickTrue;
3894 target_image=CloneImage(image,0,0,MagickTrue,exception);
3895 if (target_image == (Image *) NULL)
3896 return((Image *) NULL);
3900 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3901 if (reconstruct_image == (Image *) NULL)
3902 ThrowDPCSimilarityException();
3906 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3908 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3909 if (rx_image == (Image *) NULL)
3910 ThrowDPCSimilarityException();
3911 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3912 reconstruct_image=DestroyImage(reconstruct_image);
3913 if (ry_image == (Image *) NULL)
3914 ThrowDPCSimilarityException();
3918 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3919 if (magnitude_image == (Image *) NULL)
3920 ThrowDPCSimilarityException();
3924 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3925 if (threshold_image == (Image *) NULL)
3926 ThrowDPCSimilarityException();
3927 status=BilevelImage(threshold_image,0.0,exception);
3928 if (status == MagickFalse)
3929 ThrowDPCSimilarityException();
3930 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3931 threshold_image=DestroyImage(threshold_image);
3932 if (status == MagickFalse)
3933 ThrowDPCSimilarityException();
3934 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
3935 reconstruct->rows)+QuantumScale;
3939 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3940 rx_image=DestroyImage(rx_image);
3941 if (trx_image == (Image *) NULL)
3942 ThrowDPCSimilarityException();
3944 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3945 magnitude_image=DestroyImage(magnitude_image);
3946 ry_image=DestroyImage(ry_image);
3947 if (try_image == (Image *) NULL)
3948 ThrowDPCSimilarityException();
3953 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
3955 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
3956 if (tx_image == (Image *) NULL)
3957 ThrowDPCSimilarityException();
3958 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
3959 target_image=DestroyImage(target_image);
3960 if (ty_image == (Image *) NULL)
3961 ThrowDPCSimilarityException();
3965 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
3966 if (magnitude_image == (Image *) NULL)
3967 ThrowDPCSimilarityException();
3971 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
3972 tx_image=DestroyImage(tx_image);
3973 if (trx_image == (Image *) NULL)
3974 ThrowDPCSimilarityException();
3976 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
3977 ty_image=DestroyImage(ty_image);
3978 magnitude_image=DestroyImage(magnitude_image);
3979 if (try_image == (Image *) NULL)
3980 ThrowDPCSimilarityException();
3985 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
3986 rx_image=DestroyImage(rx_image);
3987 tx_image=DestroyImage(tx_image);
3988 if (trx_image == (Image *) NULL)
3989 ThrowDPCSimilarityException();
3990 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
3991 ry_image=DestroyImage(ry_image);
3992 ty_image=DestroyImage(ty_image);
3993 if (try_image == (Image *) NULL)
3994 ThrowDPCSimilarityException();
3998 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
3999 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
4001 try_image=DestroyImage(try_image);
4002 if (status == MagickFalse)
4003 ThrowDPCSimilarityException();
4004 status=SIMMultiplyImage(trx_image,edge_factor,
4005 (
const ChannelStatistics *) NULL,exception);
4006 if (status == MagickFalse)
4007 ThrowDPCSimilarityException();
4011 SetGeometry(image,&geometry);
4012 geometry.width=image->columns;
4013 geometry.height=image->rows;
4014 (void) ResetImagePage(trx_image,
"0x0+0+0");
4015 dot_product_image=CropImage(trx_image,&geometry,exception);
4016 trx_image=DestroyImage(trx_image);
4017 if (dot_product_image == (Image *) NULL)
4018 ThrowDPCSimilarityException();
4019 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4023 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4025 if (status == MagickFalse)
4026 ThrowDPCSimilarityException();
4027 dot_product_image->depth=32;
4028 dot_product_image->colorspace=GRAYColorspace;
4029 dot_product_image->alpha_trait=UndefinedPixelTrait;
4030 status=SIMFilterImageNaNs(dot_product_image,exception);
4031 if (status == MagickFalse)
4032 ThrowDPCSimilarityException();
4033 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4034 if (status == MagickFalse)
4035 ThrowDPCSimilarityException();
4036 if ((QuantumScale*maxima) > 1.0)
4038 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4039 (
const ChannelStatistics *) NULL,exception);
4040 maxima=(double) QuantumRange;
4042 *similarity_metric=QuantumScale*maxima;
4043 return(dot_product_image);
4046static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4047 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4049#define ThrowMSESimilarityException() \
4051 if (alpha_image != (Image *) NULL) \
4052 alpha_image=DestroyImage(alpha_image); \
4053 if (beta_image != (Image *) NULL) \
4054 beta_image=DestroyImage(beta_image); \
4055 if (channel_statistics != (ChannelStatistics *) NULL) \
4056 channel_statistics=(ChannelStatistics *) \
4057 RelinquishMagickMemory(channel_statistics); \
4058 if (mean_image != (Image *) NULL) \
4059 mean_image=DestroyImage(mean_image); \
4060 if (mse_image != (Image *) NULL) \
4061 mse_image=DestroyImage(mse_image); \
4062 if (reconstruct_image != (Image *) NULL) \
4063 reconstruct_image=DestroyImage(reconstruct_image); \
4064 if (sum_image != (Image *) NULL) \
4065 sum_image=DestroyImage(sum_image); \
4066 if (alpha_image != (Image *) NULL) \
4067 alpha_image=DestroyImage(alpha_image); \
4068 return((Image *) NULL); \
4072 *channel_statistics = (ChannelStatistics *) NULL;
4078 *alpha_image = (Image *) NULL,
4079 *beta_image = (Image *) NULL,
4080 *mean_image = (Image *) NULL,
4081 *mse_image = (Image *) NULL,
4082 *reconstruct_image = (Image *) NULL,
4083 *sum_image = (Image *) NULL,
4084 *target_image = (Image *) NULL;
4087 status = MagickTrue;
4095 target_image=SIMSquareImage(image,exception);
4096 if (target_image == (Image *) NULL)
4097 ThrowMSESimilarityException();
4098 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4099 if (reconstruct_image == (Image *) NULL)
4100 ThrowMSESimilarityException();
4104 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4106 target_image=DestroyImage(target_image);
4107 if (alpha_image == (Image *) NULL)
4108 ThrowMSESimilarityException();
4109 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4110 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4111 if (status == MagickFalse)
4112 ThrowMSESimilarityException();
4116 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4117 MagickTrue,0,0,exception);
4118 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4119 if (beta_image == (Image *) NULL)
4121 reconstruct_image=DestroyImage(reconstruct_image);
4122 ThrowMSESimilarityException();
4124 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4125 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4126 reconstruct_image=DestroyImage(reconstruct_image);
4127 if (status == MagickFalse)
4128 ThrowMSESimilarityException();
4132 sum_image=SIMSquareImage(reconstruct,exception);
4133 if (sum_image == (Image *) NULL)
4134 ThrowMSESimilarityException();
4135 channel_statistics=GetImageStatistics(sum_image,exception);
4136 if (channel_statistics == (ChannelStatistics *) NULL)
4137 ThrowMSESimilarityException();
4138 status=SetImageStorageClass(sum_image,DirectClass,exception);
4139 if (status == MagickFalse)
4140 ThrowMSESimilarityException();
4141 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4142 channel_statistics=(ChannelStatistics *)
4143 RelinquishMagickMemory(channel_statistics);
4144 if (status == MagickFalse)
4145 ThrowMSESimilarityException();
4149 AppendImageToList(&sum_image,alpha_image);
4150 AppendImageToList(&sum_image,beta_image);
4151 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4152 if (mean_image == (Image *) NULL)
4153 ThrowMSESimilarityException();
4154 sum_image=DestroyImage(sum_image);
4155 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4156 if (status == MagickFalse)
4157 ThrowMSESimilarityException();
4161 SetGeometry(image,&geometry);
4162 geometry.width=image->columns;
4163 geometry.height=image->rows;
4164 (void) ResetImagePage(mean_image,
"0x0+0+0");
4165 mse_image=CropImage(mean_image,&geometry,exception);
4166 mean_image=DestroyImage(mean_image);
4167 if (mse_image == (Image *) NULL)
4168 ThrowMSESimilarityException();
4172 (void) ResetImagePage(mse_image,
"0x0+0+0");
4173 (void) ClampImage(mse_image,exception);
4174 mse_image->depth=32;
4175 mse_image->colorspace=GRAYColorspace;
4176 mse_image->alpha_trait=UndefinedPixelTrait;
4177 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4178 if (status == MagickFalse)
4179 ThrowMSESimilarityException();
4180 status=NegateImage(mse_image,MagickFalse,exception);
4181 if (status == MagickFalse)
4182 ThrowMSESimilarityException();
4183 alpha_image=DestroyImage(alpha_image);
4184 beta_image=DestroyImage(beta_image);
4185 if ((QuantumScale*minima) < FLT_EPSILON)
4187 *similarity_metric=QuantumScale*minima;
4191static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4192 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4194#define ThrowNCCSimilarityException() \
4196 if (alpha_image != (Image *) NULL) \
4197 alpha_image=DestroyImage(alpha_image); \
4198 if (beta_image != (Image *) NULL) \
4199 beta_image=DestroyImage(beta_image); \
4200 if (channel_statistics != (ChannelStatistics *) NULL) \
4201 channel_statistics=(ChannelStatistics *) \
4202 RelinquishMagickMemory(channel_statistics); \
4203 if (correlation_image != (Image *) NULL) \
4204 correlation_image=DestroyImage(correlation_image); \
4205 if (divide_image != (Image *) NULL) \
4206 divide_image=DestroyImage(divide_image); \
4207 if (ncc_image != (Image *) NULL) \
4208 ncc_image=DestroyImage(ncc_image); \
4209 if (normalize_image != (Image *) NULL) \
4210 normalize_image=DestroyImage(normalize_image); \
4211 if (reconstruct_image != (Image *) NULL) \
4212 reconstruct_image=DestroyImage(reconstruct_image); \
4213 if (target_image != (Image *) NULL) \
4214 target_image=DestroyImage(target_image); \
4215 if (variance_image != (Image *) NULL) \
4216 variance_image=DestroyImage(variance_image); \
4217 return((Image *) NULL); \
4221 *channel_statistics = (ChannelStatistics *) NULL;
4227 *alpha_image = (Image *) NULL,
4228 *beta_image = (Image *) NULL,
4229 *correlation_image = (Image *) NULL,
4230 *divide_image = (Image *) NULL,
4231 *ncc_image = (Image *) NULL,
4232 *normalize_image = (Image *) NULL,
4233 *reconstruct_image = (Image *) NULL,
4234 *target_image = (Image *) NULL,
4235 *variance_image = (Image *) NULL;
4238 status = MagickTrue;
4246 target_image=SIMSquareImage(image,exception);
4247 if (target_image == (Image *) NULL)
4248 ThrowNCCSimilarityException();
4249 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4250 if (reconstruct_image == (Image *) NULL)
4251 ThrowNCCSimilarityException();
4255 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4257 target_image=DestroyImage(target_image);
4258 if (alpha_image == (Image *) NULL)
4259 ThrowNCCSimilarityException();
4260 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4261 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4263 if (status == MagickFalse)
4264 ThrowNCCSimilarityException();
4268 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4269 reconstruct_image=DestroyImage(reconstruct_image);
4270 if (beta_image == (Image *) NULL)
4271 ThrowNCCSimilarityException();
4272 target_image=SIMSquareImage(beta_image,exception);
4273 beta_image=DestroyImage(beta_image);
4274 if (target_image == (Image *) NULL)
4275 ThrowNCCSimilarityException();
4276 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4277 (
const ChannelStatistics *) NULL,exception);
4278 if (status == MagickFalse)
4279 ThrowNCCSimilarityException();
4283 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4284 target_image=DestroyImage(target_image);
4285 alpha_image=DestroyImage(alpha_image);
4286 if (variance_image == (Image *) NULL)
4287 ThrowNCCSimilarityException();
4291 channel_statistics=GetImageStatistics(reconstruct,exception);
4292 if (channel_statistics == (ChannelStatistics *) NULL)
4293 ThrowNCCSimilarityException();
4294 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4295 if (status == MagickFalse)
4296 ThrowNCCSimilarityException();
4297 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4299 channel_statistics=(ChannelStatistics *)
4300 RelinquishMagickMemory(channel_statistics);
4301 if (normalize_image == (Image *) NULL)
4302 ThrowNCCSimilarityException();
4303 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4304 normalize_image=DestroyImage(normalize_image);
4305 if (correlation_image == (Image *) NULL)
4306 ThrowNCCSimilarityException();
4310 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4311 correlation_image=DestroyImage(correlation_image);
4312 variance_image=DestroyImage(variance_image);
4313 if (divide_image == (Image *) NULL)
4314 ThrowNCCSimilarityException();
4318 SetGeometry(image,&geometry);
4319 geometry.width=image->columns;
4320 geometry.height=image->rows;
4321 (void) ResetImagePage(divide_image,
"0x0+0+0");
4322 ncc_image=CropImage(divide_image,&geometry,exception);
4323 divide_image=DestroyImage(divide_image);
4324 if (ncc_image == (Image *) NULL)
4325 ThrowNCCSimilarityException();
4329 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4330 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4331 if (status == MagickFalse)
4332 ThrowNCCSimilarityException();
4333 ncc_image->depth=32;
4334 ncc_image->colorspace=GRAYColorspace;
4335 ncc_image->alpha_trait=UndefinedPixelTrait;
4336 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4337 if (status == MagickFalse)
4338 ThrowNCCSimilarityException();
4339 if ((QuantumScale*maxima) > 1.0)
4341 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4342 (
const ChannelStatistics *) NULL,exception);
4343 maxima=(double) QuantumRange;
4345 *similarity_metric=QuantumScale*maxima;
4349static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4350 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4352#define ThrowPhaseSimilarityException() \
4354 if (correlation_image != (Image *) NULL) \
4355 correlation_image=DestroyImage(correlation_image); \
4356 if (fft_images != (Image *) NULL) \
4357 fft_images=DestroyImageList(fft_images); \
4358 if (gamma_image != (Image *) NULL) \
4359 gamma_image=DestroyImage(gamma_image); \
4360 if (magnitude_image != (Image *) NULL) \
4361 magnitude_image=DestroyImage(magnitude_image); \
4362 if (phase_image != (Image *) NULL) \
4363 phase_image=DestroyImage(phase_image); \
4364 if (reconstruct_image != (Image *) NULL) \
4365 reconstruct_image=DestroyImage(reconstruct_image); \
4366 if (reconstruct_magnitude != (Image *) NULL) \
4367 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4368 if (target_image != (Image *) NULL) \
4369 target_image=DestroyImage(target_image); \
4370 if (test_magnitude != (Image *) NULL) \
4371 test_magnitude=DestroyImage(test_magnitude); \
4372 return((Image *) NULL); \
4379 *correlation_image = (Image *) NULL,
4380 *fft_images = (Image *) NULL,
4381 *gamma_image = (Image *) NULL,
4382 *magnitude_image = (Image *) NULL,
4383 *phase_image = (Image *) NULL,
4384 *reconstruct_image = (Image *) NULL,
4385 *reconstruct_magnitude = (Image *) NULL,
4386 *target_image = (Image *) NULL,
4387 *test_magnitude = (Image *) NULL;
4390 status = MagickTrue;
4398 target_image=CloneImage(image,0,0,MagickTrue,exception);
4399 if (target_image == (Image *) NULL)
4400 ThrowPhaseSimilarityException();
4401 (void) ResetImagePage(target_image,
"0x0+0+0");
4402 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4403 &target_image->background_color);
4404 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4405 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4406 if (status == MagickFalse)
4407 ThrowPhaseSimilarityException();
4411 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4412 if (reconstruct_image == (Image *) NULL)
4413 ThrowPhaseSimilarityException();
4414 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4415 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4416 &reconstruct_image->background_color);
4417 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4418 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4419 if (status == MagickFalse)
4420 ThrowPhaseSimilarityException();
4424 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4425 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4426 if (fft_images == (Image *) NULL)
4427 ThrowPhaseSimilarityException();
4428 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4429 fft_images=DestroyImageList(fft_images);
4430 if (test_magnitude == (Image *) NULL)
4431 ThrowPhaseSimilarityException();
4432 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4433 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4435 if (fft_images == (Image *) NULL)
4436 ThrowPhaseSimilarityException();
4437 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4438 fft_images=DestroyImageList(fft_images);
4439 if (reconstruct_magnitude == (Image *) NULL)
4440 ThrowPhaseSimilarityException();
4441 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4442 if (magnitude_image == (Image *) NULL)
4443 ThrowPhaseSimilarityException();
4444 DisableCompositeClampUnlessSpecified(magnitude_image);
4445 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4446 MagickTrue,0,0,exception);
4450 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4451 magnitude_image,exception);
4452 target_image=DestroyImage(target_image);
4453 reconstruct_image=DestroyImage(reconstruct_image);
4454 test_magnitude=DestroyImage(test_magnitude);
4455 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4456 if (correlation_image == (Image *) NULL)
4457 ThrowPhaseSimilarityException();
4461 status=SIMMultiplyImage(correlation_image,(
double) QuantumScale,
4462 (
const ChannelStatistics *) NULL,exception);
4463 if (status == MagickFalse)
4464 ThrowPhaseSimilarityException();
4465 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4466 correlation_image=DestroyImage(correlation_image);
4467 if (gamma_image == (Image *) NULL)
4468 ThrowPhaseSimilarityException();
4472 SetGeometry(image,&geometry);
4473 geometry.width=image->columns;
4474 geometry.height=image->rows;
4475 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4476 phase_image=CropImage(gamma_image,&geometry,exception);
4477 gamma_image=DestroyImage(gamma_image);
4478 if (phase_image == (Image *) NULL)
4479 ThrowPhaseSimilarityException();
4480 (void) ResetImagePage(phase_image,
"0x0+0+0");
4484 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4485 if (status == MagickFalse)
4486 ThrowPhaseSimilarityException();
4487 phase_image->depth=32;
4488 phase_image->colorspace=GRAYColorspace;
4489 phase_image->alpha_trait=UndefinedPixelTrait;
4490 status=SIMFilterImageNaNs(phase_image,exception);
4491 if (status == MagickFalse)
4492 ThrowPhaseSimilarityException();
4493 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4494 if (status == MagickFalse)
4495 ThrowPhaseSimilarityException();
4496 magnitude_image=DestroyImage(magnitude_image);
4497 *similarity_metric=QuantumScale*maxima;
4498 return(phase_image);
4501static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4502 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4505 *psnr_image = (Image *) NULL;
4507 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4509 if (psnr_image == (Image *) NULL)
4511 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4512 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4516static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4517 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4520 *rmse_image = (Image *) NULL;
4522 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4524 if (rmse_image == (Image *) NULL)
4526 *similarity_metric=sqrt(*similarity_metric);
4531static double GetSimilarityMetric(
const Image *image,
4532 const Image *reconstruct_image,
const MetricType metric,
4533 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4536 *channel_similarity,
4540 *sans_exception = AcquireExceptionInfo();
4546 status = MagickTrue;
4552 length = MaxPixelChannels+1UL;
4554 SetGeometry(reconstruct_image,&geometry);
4555 geometry.x=x_offset;
4556 geometry.y=y_offset;
4557 similarity_image=CropImage(image,&geometry,sans_exception);
4558 sans_exception=DestroyExceptionInfo(sans_exception);
4559 if (similarity_image == (Image *) NULL)
4564 channel_similarity=(
double *) AcquireQuantumMemory(length,
4565 sizeof(*channel_similarity));
4566 if (channel_similarity == (
double *) NULL)
4567 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4568 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4571 case AbsoluteErrorMetric:
4573 status=GetAESimilarity(similarity_image,reconstruct_image,
4574 channel_similarity,exception);
4577 case DotProductCorrelationErrorMetric:
4579 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4580 channel_similarity,exception);
4583 case FuzzErrorMetric:
4585 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4586 channel_similarity,exception);
4589 case MeanAbsoluteErrorMetric:
4591 status=GetMAESimilarity(similarity_image,reconstruct_image,
4592 channel_similarity,exception);
4595 case MeanErrorPerPixelErrorMetric:
4597 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4598 channel_similarity,exception);
4601 case MeanSquaredErrorMetric:
4603 status=GetMSESimilarity(similarity_image,reconstruct_image,
4604 channel_similarity,exception);
4607 case NormalizedCrossCorrelationErrorMetric:
4609 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4610 channel_similarity,exception);
4613 case PeakAbsoluteErrorMetric:
4615 status=GetPASimilarity(similarity_image,reconstruct_image,
4616 channel_similarity,exception);
4619 case PeakSignalToNoiseRatioErrorMetric:
4621 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4622 channel_similarity,exception);
4625 case PerceptualHashErrorMetric:
4627 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4628 channel_similarity,exception);
4631 case PhaseCorrelationErrorMetric:
4633 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4634 channel_similarity,exception);
4637 case PixelDifferenceCountErrorMetric:
4639 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4640 channel_similarity,exception);
4643 case RootMeanSquaredErrorMetric:
4644 case UndefinedErrorMetric:
4647 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4648 channel_similarity,exception);
4651 case StructuralDissimilarityErrorMetric:
4653 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4654 channel_similarity,exception);
4657 case StructuralSimilarityErrorMetric:
4659 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4660 channel_similarity,exception);
4664 similarity_image=DestroyImage(similarity_image);
4665 similarity=channel_similarity[CompositePixelChannel];
4666 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4667 if (status == MagickFalse)
4672MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4673 const MetricType metric,
const double similarity_threshold,
4674 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4676#define SimilarityImageTag "Similarity/Image"
4692 *similarity_image = (Image *) NULL;
4695 status = MagickTrue;
4701 similarity_info = { 0.0, 0, 0 };
4710 assert(image != (
const Image *) NULL);
4711 assert(image->signature == MagickCoreSignature);
4712 assert(exception != (ExceptionInfo *) NULL);
4713 assert(exception->signature == MagickCoreSignature);
4714 assert(offset != (RectangleInfo *) NULL);
4715 if (IsEventLogging() != MagickFalse)
4716 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4717 SetGeometry(reconstruct,offset);
4718 *similarity_metric=0.0;
4721#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4723 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4724 if (artifact == (
const char *) NULL)
4725 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4726 if (((artifact == (
const char *) NULL) ||
4727 (IsStringTrue(artifact) != MagickFalse)) &&
4728 ((image->channels & ReadMaskChannel) == 0))
4731 case DotProductCorrelationErrorMetric:
4733 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4734 similarity_metric,exception);
4735 return(similarity_image);
4737 case MeanSquaredErrorMetric:
4739 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4740 similarity_metric,exception);
4741 return(similarity_image);
4743 case NormalizedCrossCorrelationErrorMetric:
4745 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4746 similarity_metric,exception);
4747 return(similarity_image);
4749 case PeakSignalToNoiseRatioErrorMetric:
4751 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4752 similarity_metric,exception);
4753 return(similarity_image);
4755 case PhaseCorrelationErrorMetric:
4757 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4758 similarity_metric,exception);
4759 return(similarity_image);
4761 case RootMeanSquaredErrorMetric:
4762 case UndefinedErrorMetric:
4764 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4765 similarity_metric,exception);
4766 return(similarity_image);
4773 if ((image->columns < reconstruct->columns) ||
4774 (image->rows < reconstruct->rows))
4776 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4777 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4778 return((Image *) NULL);
4780 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4781 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4782 if (similarity_image == (Image *) NULL)
4783 return((Image *) NULL);
4784 similarity_image->depth=32;
4785 similarity_image->colorspace=GRAYColorspace;
4786 similarity_image->alpha_trait=UndefinedPixelTrait;
4787 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4788 if (status == MagickFalse)
4789 return(DestroyImage(similarity_image));
4793 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4794 similarity_info.x,similarity_info.y,exception);
4795 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4796#if defined(MAGICKCORE_OPENMP_SUPPORT)
4797 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4798 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4800 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4806 threshold_trigger = MagickFalse;
4812 channel_info = similarity_info;
4817 if (status == MagickFalse)
4819 if (threshold_trigger != MagickFalse)
4821 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4822 similarity_image->columns,1,exception);
4823 if (q == (Quantum *) NULL)
4828 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4833 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4837 case DotProductCorrelationErrorMetric:
4838 case NormalizedCrossCorrelationErrorMetric:
4839 case PeakSignalToNoiseRatioErrorMetric:
4840 case PhaseCorrelationErrorMetric:
4841 case StructuralSimilarityErrorMetric:
4843 if (similarity <= channel_info.similarity)
4845 channel_info.similarity=similarity;
4852 if (similarity >= channel_info.similarity)
4854 channel_info.similarity=similarity;
4860 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4862 PixelChannel channel = GetPixelChannelChannel(image,i);
4863 PixelTrait traits = GetPixelChannelTraits(image,channel);
4864 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4866 if (((traits & UpdatePixelTrait) == 0) ||
4867 ((similarity_traits & UpdatePixelTrait) == 0))
4871 case DotProductCorrelationErrorMetric:
4872 case NormalizedCrossCorrelationErrorMetric:
4873 case PeakSignalToNoiseRatioErrorMetric:
4874 case PhaseCorrelationErrorMetric:
4875 case StructuralSimilarityErrorMetric:
4877 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4878 QuantumRange*similarity),q);
4883 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4884 QuantumRange*(1.0-similarity)),q);
4889 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4891#if defined(MAGICKCORE_OPENMP_SUPPORT)
4892 #pragma omp critical (MagickCore_GetSimilarityMetric)
4896 case DotProductCorrelationErrorMetric:
4897 case NormalizedCrossCorrelationErrorMetric:
4898 case PeakSignalToNoiseRatioErrorMetric:
4899 case PhaseCorrelationErrorMetric:
4900 case StructuralSimilarityErrorMetric:
4902 if (similarity_threshold != DefaultSimilarityThreshold)
4903 if (channel_info.similarity >= similarity_threshold)
4904 threshold_trigger=MagickTrue;
4905 if (channel_info.similarity >= similarity_info.similarity)
4906 similarity_info=channel_info;
4911 if (similarity_threshold != DefaultSimilarityThreshold)
4912 if (channel_info.similarity < similarity_threshold)
4913 threshold_trigger=MagickTrue;
4914 if (channel_info.similarity < similarity_info.similarity)
4915 similarity_info=channel_info;
4919 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4921 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4927 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4928 if (proceed == MagickFalse)
4932 similarity_view=DestroyCacheView(similarity_view);
4933 if (status == MagickFalse)
4934 similarity_image=DestroyImage(similarity_image);
4935 *similarity_metric=similarity_info.similarity;
4936 if (fabs(*similarity_metric) < MagickEpsilon)
4937 *similarity_metric=0.0;
4938 offset->x=similarity_info.x;
4939 offset->y=similarity_info.y;
4940 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4941 GetMagickPrecision(),*similarity_metric);
4942 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4943 GetMagickPrecision(),(
double) offset->x);
4944 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4945 GetMagickPrecision(),(
double) offset->y);
4946 return(similarity_image);