43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/channel.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/compare.h"
54#include "MagickCore/compare-private.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/fourier.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/registry.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/statistic.h"
75#include "MagickCore/statistic-private.h"
76#include "MagickCore/string-private.h"
77#include "MagickCore/thread-private.h"
78#include "MagickCore/threshold.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/utility.h"
81#include "MagickCore/version.h"
115MagickExport Image *CompareImages(Image *image,
const Image *reconstruct_image,
116 const MetricType metric,
double *distortion,ExceptionInfo *exception)
149 assert(image != (Image *) NULL);
150 assert(image->signature == MagickCoreSignature);
151 assert(reconstruct_image != (
const Image *) NULL);
152 assert(reconstruct_image->signature == MagickCoreSignature);
153 assert(distortion != (
double *) NULL);
154 if (IsEventLogging() != MagickFalse)
155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
157 status=GetImageDistortion(image,reconstruct_image,metric,distortion,
159 if (status == MagickFalse)
160 return((Image *) NULL);
161 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
162 SetGeometry(image,&geometry);
163 geometry.width=columns;
164 geometry.height=rows;
165 clone_image=CloneImage(image,0,0,MagickTrue,exception);
166 if (clone_image == (Image *) NULL)
167 return((Image *) NULL);
168 (void) SetImageMask(clone_image,ReadPixelMask,(Image *) NULL,exception);
169 difference_image=ExtentImage(clone_image,&geometry,exception);
170 clone_image=DestroyImage(clone_image);
171 if (difference_image == (Image *) NULL)
172 return((Image *) NULL);
173 (void) ResetImagePage(difference_image,
"0x0+0+0");
174 (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel,exception);
175 highlight_image=CloneImage(image,columns,rows,MagickTrue,exception);
176 if (highlight_image == (Image *) NULL)
178 difference_image=DestroyImage(difference_image);
179 return((Image *) NULL);
181 status=SetImageStorageClass(highlight_image,DirectClass,exception);
182 if (status == MagickFalse)
184 difference_image=DestroyImage(difference_image);
185 highlight_image=DestroyImage(highlight_image);
186 return((Image *) NULL);
188 (void) SetImageMask(highlight_image,ReadPixelMask,(Image *) NULL,exception);
189 (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel,exception);
190 (void) QueryColorCompliance(
"#f1001ecc",AllCompliance,&highlight,exception);
191 artifact=GetImageArtifact(image,
"compare:highlight-color");
192 if (artifact != (
const char *) NULL)
193 (void) QueryColorCompliance(artifact,AllCompliance,&highlight,exception);
194 (void) QueryColorCompliance(
"#ffffffcc",AllCompliance,&lowlight,exception);
195 artifact=GetImageArtifact(image,
"compare:lowlight-color");
196 if (artifact != (
const char *) NULL)
197 (void) QueryColorCompliance(artifact,AllCompliance,&lowlight,exception);
198 (void) QueryColorCompliance(
"#888888cc",AllCompliance,&masklight,exception);
199 artifact=GetImageArtifact(image,
"compare:masklight-color");
200 if (artifact != (
const char *) NULL)
201 (void) QueryColorCompliance(artifact,AllCompliance,&masklight,exception);
205 image_view=AcquireVirtualCacheView(image,exception);
206 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
207 highlight_view=AcquireAuthenticCacheView(highlight_image,exception);
208#if defined(MAGICKCORE_OPENMP_SUPPORT)
209 #pragma omp parallel for schedule(static) shared(status) \
210 magick_number_threads(image,highlight_image,rows,1)
212 for (y=0; y < (ssize_t) rows; y++)
227 if (status == MagickFalse)
229 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
230 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
231 r=QueueCacheViewAuthenticPixels(highlight_view,0,y,columns,1,exception);
232 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL) ||
233 (r == (Quantum *) NULL))
238 for (x=0; x < (ssize_t) columns; x++)
240 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
241 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
243 SetPixelViaPixelInfo(highlight_image,&masklight,r);
244 p+=(ptrdiff_t) GetPixelChannels(image);
245 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
246 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
249 if (IsFuzzyEquivalencePixel(image,p,reconstruct_image,q) == MagickFalse)
250 SetPixelViaPixelInfo(highlight_image,&highlight,r);
252 SetPixelViaPixelInfo(highlight_image,&lowlight,r);
253 p+=(ptrdiff_t) GetPixelChannels(image);
254 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
255 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
257 sync=SyncCacheViewAuthenticPixels(highlight_view,exception);
258 if (sync == MagickFalse)
261 highlight_view=DestroyCacheView(highlight_view);
262 reconstruct_view=DestroyCacheView(reconstruct_view);
263 image_view=DestroyCacheView(image_view);
264 if ((status != MagickFalse) && (difference_image != (Image *) NULL))
265 status=CompositeImage(difference_image,highlight_image,image->compose,
266 MagickTrue,0,0,exception);
267 highlight_image=DestroyImage(highlight_image);
268 if (status == MagickFalse)
269 difference_image=DestroyImage(difference_image);
270 return(difference_image);
307static MagickBooleanType GetAESimilarity(
const Image *image,
308 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
332 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
333 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
334 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
335 image_view=AcquireVirtualCacheView(image,exception);
336 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
337#if defined(MAGICKCORE_OPENMP_SUPPORT)
338 #pragma omp parallel for schedule(static) shared(similarity,status) \
339 magick_number_threads(image,image,rows,1)
341 for (y=0; y < (ssize_t) rows; y++)
348 channel_similarity[MaxPixelChannels+1] = { 0.0 };
353 if (status == MagickFalse)
355 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
356 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
357 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
362 for (x=0; x < (ssize_t) columns; x++)
374 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
375 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
377 p+=(ptrdiff_t) GetPixelChannels(image);
378 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
381 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
382 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
383 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
388 PixelChannel channel = GetPixelChannelChannel(image,i);
389 PixelTrait traits = GetPixelChannelTraits(image,channel);
390 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
392 if (((traits & UpdatePixelTrait) == 0) ||
393 ((reconstruct_traits & UpdatePixelTrait) == 0))
395 if (channel == AlphaPixelChannel)
396 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
399 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
400 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
402 channel_similarity[i]++;
407 channel_similarity[CompositePixelChannel]++;
408 p+=(ptrdiff_t) GetPixelChannels(image);
409 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
411#if defined(MAGICKCORE_OPENMP_SUPPORT)
412 #pragma omp critical (MagickCore_GetAESimilarity)
418 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
420 PixelChannel channel = GetPixelChannelChannel(image,j);
421 PixelTrait traits = GetPixelChannelTraits(image,channel);
422 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
424 if (((traits & UpdatePixelTrait) == 0) ||
425 ((reconstruct_traits & UpdatePixelTrait) == 0))
427 similarity[j]+=channel_similarity[j];
429 similarity[CompositePixelChannel]+=
430 channel_similarity[CompositePixelChannel];
433 reconstruct_view=DestroyCacheView(reconstruct_view);
434 image_view=DestroyCacheView(image_view);
435 area=MagickSafeReciprocal((
double) columns*rows);
436 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
438 similarity[CompositePixelChannel]*=area;
442static MagickBooleanType GetDPCSimilarity(
const Image *image,
443 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
445#define SimilarityImageTag "Similarity/Image"
453 *reconstruct_statistics;
456 norm[MaxPixelChannels+1] = { 0.0 },
457 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
476 image_statistics=GetImageStatistics(image,exception);
477 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
478 if ((image_statistics == (ChannelStatistics *) NULL) ||
479 (reconstruct_statistics == (ChannelStatistics *) NULL))
481 if (image_statistics != (ChannelStatistics *) NULL)
482 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
484 if (reconstruct_statistics != (ChannelStatistics *) NULL)
485 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
486 reconstruct_statistics);
489 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
490 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
491 image_view=AcquireVirtualCacheView(image,exception);
492 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
493#if defined(MAGICKCORE_OPENMP_SUPPORT)
494 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
495 magick_number_threads(image,image,rows,1)
497 for (y=0; y < (ssize_t) rows; y++)
504 channel_norm[MaxPixelChannels+1] = { 0.0 },
505 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
506 channel_similarity[MaxPixelChannels+1] = { 0.0 };
511 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
512 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
513 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
518 for (x=0; x < (ssize_t) columns; x++)
527 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
528 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
530 p+=(ptrdiff_t) GetPixelChannels(image);
531 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
534 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
535 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
536 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
542 PixelChannel channel = GetPixelChannelChannel(image,i);
543 PixelTrait traits = GetPixelChannelTraits(image,channel);
544 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
546 if (((traits & UpdatePixelTrait) == 0) ||
547 ((reconstruct_traits & UpdatePixelTrait) == 0))
549 if (channel == AlphaPixelChannel)
551 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
552 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
553 channel,q)-reconstruct_statistics[channel].mean);
557 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
558 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
559 q)-reconstruct_statistics[channel].mean);
561 channel_similarity[i]+=alpha*beta;
562 channel_norm[i]+=alpha*alpha;
563 channel_reconstruct_norm[i]+=beta*beta;
565 p+=(ptrdiff_t) GetPixelChannels(image);
566 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
568#if defined(MAGICKCORE_OPENMP_SUPPORT)
569 #pragma omp critical (MagickCore_GetDPCSimilarity)
575 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
577 PixelChannel channel = GetPixelChannelChannel(image,j);
578 PixelTrait traits = GetPixelChannelTraits(image,channel);
579 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
581 if (((traits & UpdatePixelTrait) == 0) ||
582 ((reconstruct_traits & UpdatePixelTrait) == 0))
584 similarity[j]+=channel_similarity[j];
585 similarity[CompositePixelChannel]+=channel_similarity[j];
586 norm[j]+=channel_norm[j];
587 norm[CompositePixelChannel]+=channel_norm[j];
588 reconstruct_norm[j]+=channel_reconstruct_norm[j];
589 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
592 if (image->progress_monitor != (MagickProgressMonitor) NULL)
597#if defined(MAGICKCORE_OPENMP_SUPPORT)
601 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
602 if (proceed == MagickFalse)
609 reconstruct_view=DestroyCacheView(reconstruct_view);
610 image_view=DestroyCacheView(image_view);
614 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
616 PixelChannel channel = GetPixelChannelChannel(image,k);
617 PixelTrait traits = GetPixelChannelTraits(image,channel);
618 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
620 if (((traits & UpdatePixelTrait) == 0) ||
621 ((reconstruct_traits & UpdatePixelTrait) == 0))
623 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
625 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
626 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
630 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
631 reconstruct_statistics);
632 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
637static MagickBooleanType GetFUZZSimilarity(
const Image *image,
638 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
662 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
663 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
664 image_view=AcquireVirtualCacheView(image,exception);
665 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
666#if defined(MAGICKCORE_OPENMP_SUPPORT)
667 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
668 magick_number_threads(image,image,rows,1)
670 for (y=0; y < (ssize_t) rows; y++)
678 channel_similarity[MaxPixelChannels+1] = { 0.0 };
683 if (status == MagickFalse)
685 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
686 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
687 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
692 for (x=0; x < (ssize_t) columns; x++)
701 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
702 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
704 p+=(ptrdiff_t) GetPixelChannels(image);
705 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
708 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
709 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
710 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
715 PixelChannel channel = GetPixelChannelChannel(image,i);
716 PixelTrait traits = GetPixelChannelTraits(image,channel);
717 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
719 if (((traits & UpdatePixelTrait) == 0) ||
720 ((reconstruct_traits & UpdatePixelTrait) == 0))
722 if (channel == AlphaPixelChannel)
723 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
726 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
727 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
729 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
730 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
735 p+=(ptrdiff_t) GetPixelChannels(image);
736 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
738#if defined(MAGICKCORE_OPENMP_SUPPORT)
739 #pragma omp critical (MagickCore_GetFUZZSimilarity)
746 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
748 PixelChannel channel = GetPixelChannelChannel(image,j);
749 PixelTrait traits = GetPixelChannelTraits(image,channel);
750 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
752 if (((traits & UpdatePixelTrait) == 0) ||
753 ((reconstruct_traits & UpdatePixelTrait) == 0))
755 similarity[j]+=channel_similarity[j];
757 similarity[CompositePixelChannel]+=
758 channel_similarity[CompositePixelChannel];
761 reconstruct_view=DestroyCacheView(reconstruct_view);
762 image_view=DestroyCacheView(image_view);
763 area=MagickSafeReciprocal(area);
764 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
766 PixelChannel channel = GetPixelChannelChannel(image,k);
767 PixelTrait traits = GetPixelChannelTraits(image,channel);
768 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
770 if (((traits & UpdatePixelTrait) == 0) ||
771 ((reconstruct_traits & UpdatePixelTrait) == 0))
775 similarity[CompositePixelChannel]*=area;
779static MagickBooleanType GetMAESimilarity(
const Image *image,
780 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
803 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
804 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
805 image_view=AcquireVirtualCacheView(image,exception);
806 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
807#if defined(MAGICKCORE_OPENMP_SUPPORT)
808 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
809 magick_number_threads(image,image,rows,1)
811 for (y=0; y < (ssize_t) rows; y++)
819 channel_similarity[MaxPixelChannels+1] = { 0.0 };
824 if (status == MagickFalse)
826 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
827 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
828 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
833 for (x=0; x < (ssize_t) columns; x++)
842 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
843 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
845 p+=(ptrdiff_t) GetPixelChannels(image);
846 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
849 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
850 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
851 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
856 PixelChannel channel = GetPixelChannelChannel(image,i);
857 PixelTrait traits = GetPixelChannelTraits(image,channel);
858 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
860 if (((traits & UpdatePixelTrait) == 0) ||
861 ((reconstruct_traits & UpdatePixelTrait) == 0))
863 if (channel == AlphaPixelChannel)
864 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
865 reconstruct_image,channel,q));
867 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
869 channel_similarity[i]+=error;
870 channel_similarity[CompositePixelChannel]+=error;
873 p+=(ptrdiff_t) GetPixelChannels(image);
874 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
876#if defined(MAGICKCORE_OPENMP_SUPPORT)
877 #pragma omp critical (MagickCore_GetMAESimilarity)
884 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
886 PixelChannel channel = GetPixelChannelChannel(image,j);
887 PixelTrait traits = GetPixelChannelTraits(image,channel);
888 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
890 if (((traits & UpdatePixelTrait) == 0) ||
891 ((reconstruct_traits & UpdatePixelTrait) == 0))
893 similarity[j]+=channel_similarity[j];
895 similarity[CompositePixelChannel]+=
896 channel_similarity[CompositePixelChannel];
899 reconstruct_view=DestroyCacheView(reconstruct_view);
900 image_view=DestroyCacheView(image_view);
901 area=MagickSafeReciprocal(area);
902 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
904 PixelChannel channel = GetPixelChannelChannel(image,k);
905 PixelTrait traits = GetPixelChannelTraits(image,channel);
906 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
908 if (((traits & UpdatePixelTrait) == 0) ||
909 ((reconstruct_traits & UpdatePixelTrait) == 0))
913 similarity[CompositePixelChannel]*=area;
914 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
918static MagickBooleanType GetMEPPSimilarity(Image *image,
919 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
927 maximum_error = -MagickMaximumValue,
944 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
945 image_view=AcquireVirtualCacheView(image,exception);
946 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
947#if defined(MAGICKCORE_OPENMP_SUPPORT)
948 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
949 magick_number_threads(image,image,rows,1)
951 for (y=0; y < (ssize_t) rows; y++)
959 channel_similarity[MaxPixelChannels+1] = { 0.0 },
960 channel_maximum_error = maximum_error,
961 channel_mean_error = 0.0;
966 if (status == MagickFalse)
968 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
969 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
970 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
975 for (x=0; x < (ssize_t) columns; x++)
984 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
985 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
987 p+=(ptrdiff_t) GetPixelChannels(image);
988 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
991 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
992 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
993 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
998 PixelChannel channel = GetPixelChannelChannel(image,i);
999 PixelTrait traits = GetPixelChannelTraits(image,channel);
1000 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1002 if (((traits & UpdatePixelTrait) == 0) ||
1003 ((reconstruct_traits & UpdatePixelTrait) == 0))
1005 if (channel == AlphaPixelChannel)
1006 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
1007 reconstruct_image,channel,q));
1009 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1011 channel_similarity[i]+=error;
1012 channel_similarity[CompositePixelChannel]+=error;
1013 channel_mean_error+=error*error;
1014 if (error > channel_maximum_error)
1015 channel_maximum_error=error;
1018 p+=(ptrdiff_t) GetPixelChannels(image);
1019 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1021#if defined(MAGICKCORE_OPENMP_SUPPORT)
1022 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1029 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1031 PixelChannel channel = GetPixelChannelChannel(image,j);
1032 PixelTrait traits = GetPixelChannelTraits(image,channel);
1033 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1035 if (((traits & UpdatePixelTrait) == 0) ||
1036 ((reconstruct_traits & UpdatePixelTrait) == 0))
1038 similarity[j]+=channel_similarity[j];
1040 similarity[CompositePixelChannel]+=
1041 channel_similarity[CompositePixelChannel];
1042 mean_error+=channel_mean_error;
1043 if (channel_maximum_error > maximum_error)
1044 maximum_error=channel_maximum_error;
1047 reconstruct_view=DestroyCacheView(reconstruct_view);
1048 image_view=DestroyCacheView(image_view);
1049 area=MagickSafeReciprocal(area);
1050 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1052 PixelChannel channel = GetPixelChannelChannel(image,k);
1053 PixelTrait traits = GetPixelChannelTraits(image,channel);
1054 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1056 if (((traits & UpdatePixelTrait) == 0) ||
1057 ((reconstruct_traits & UpdatePixelTrait) == 0))
1059 similarity[k]*=area;
1061 similarity[CompositePixelChannel]*=area;
1062 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1063 image->error.mean_error_per_pixel=QuantumRange*
1064 similarity[CompositePixelChannel];
1065 image->error.normalized_mean_error=mean_error*area;
1066 image->error.normalized_maximum_error=maximum_error;
1070static MagickBooleanType GetMSESimilarity(
const Image *image,
1071 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1081 status = MagickTrue;
1094 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1095 image_view=AcquireVirtualCacheView(image,exception);
1096 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1097#if defined(MAGICKCORE_OPENMP_SUPPORT)
1098 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1099 magick_number_threads(image,image,rows,1)
1101 for (y=0; y < (ssize_t) rows; y++)
1109 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1114 if (status == MagickFalse)
1116 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1117 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1118 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1123 for (x=0; x < (ssize_t) columns; x++)
1132 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1133 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1135 p+=(ptrdiff_t) GetPixelChannels(image);
1136 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1139 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1140 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1141 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1146 PixelChannel channel = GetPixelChannelChannel(image,i);
1147 PixelTrait traits = GetPixelChannelTraits(image,channel);
1148 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1150 if (((traits & UpdatePixelTrait) == 0) ||
1151 ((reconstruct_traits & UpdatePixelTrait) == 0))
1153 if (channel == AlphaPixelChannel)
1154 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1155 reconstruct_image,channel,q));
1157 error=QuantumScale*(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1159 channel_similarity[i]+=error*error;
1160 channel_similarity[CompositePixelChannel]+=error*error;
1163 p+=(ptrdiff_t) GetPixelChannels(image);
1164 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1166#if defined(MAGICKCORE_OPENMP_SUPPORT)
1167 #pragma omp critical (MagickCore_GetMSESimilarity)
1174 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1176 PixelChannel channel = GetPixelChannelChannel(image,j);
1177 PixelTrait traits = GetPixelChannelTraits(image,channel);
1178 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1180 if (((traits & UpdatePixelTrait) == 0) ||
1181 ((reconstruct_traits & UpdatePixelTrait) == 0))
1183 similarity[j]+=channel_similarity[j];
1185 similarity[CompositePixelChannel]+=
1186 channel_similarity[CompositePixelChannel];
1189 reconstruct_view=DestroyCacheView(reconstruct_view);
1190 image_view=DestroyCacheView(image_view);
1191 area=MagickSafeReciprocal(area);
1192 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1194 PixelChannel channel = GetPixelChannelChannel(image,k);
1195 PixelTrait traits = GetPixelChannelTraits(image,channel);
1196 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1198 if (((traits & UpdatePixelTrait) == 0) ||
1199 ((reconstruct_traits & UpdatePixelTrait) == 0))
1201 similarity[k]*=area;
1203 similarity[CompositePixelChannel]*=area;
1204 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1208static MagickBooleanType GetNCCSimilarity(
const Image *image,
1209 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1217 *reconstruct_statistics;
1220 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1221 variance[MaxPixelChannels+1] = { 0.0 };
1224 status = MagickTrue;
1240 image_statistics=GetImageStatistics(image,exception);
1241 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1242 if ((image_statistics == (ChannelStatistics *) NULL) ||
1243 (reconstruct_statistics == (ChannelStatistics *) NULL))
1245 if (image_statistics != (ChannelStatistics *) NULL)
1246 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1248 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1249 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1250 reconstruct_statistics);
1251 return(MagickFalse);
1253 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1254 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1255 image_view=AcquireVirtualCacheView(image,exception);
1256 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1257#if defined(MAGICKCORE_OPENMP_SUPPORT)
1258 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1259 magick_number_threads(image,image,rows,1)
1261 for (y=0; y < (ssize_t) rows; y++)
1268 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1269 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1270 channel_variance[MaxPixelChannels+1] = { 0.0 };
1275 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1276 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1277 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1282 for (x=0; x < (ssize_t) columns; x++)
1291 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1292 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1294 p+=(ptrdiff_t) GetPixelChannels(image);
1295 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1298 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1299 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1300 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1306 PixelChannel channel = GetPixelChannelChannel(image,i);
1307 PixelTrait traits = GetPixelChannelTraits(image,channel);
1308 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1310 if (((traits & UpdatePixelTrait) == 0) ||
1311 ((reconstruct_traits & UpdatePixelTrait) == 0))
1313 if (channel == AlphaPixelChannel)
1315 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1316 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1317 channel,q)-reconstruct_statistics[channel].mean);
1321 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
1322 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
1323 q)-reconstruct_statistics[channel].mean);
1325 channel_similarity[i]+=alpha*beta;
1326 channel_variance[i]+=alpha*alpha;
1327 channel_reconstruct_variance[i]+=beta*beta;
1329 p+=(ptrdiff_t) GetPixelChannels(image);
1330 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1332#if defined(MAGICKCORE_OPENMP_SUPPORT)
1333 #pragma omp critical (MagickCore_GetNCCSimilarity)
1339 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1341 PixelChannel channel = GetPixelChannelChannel(image,j);
1342 PixelTrait traits = GetPixelChannelTraits(image,channel);
1343 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1345 if (((traits & UpdatePixelTrait) == 0) ||
1346 ((reconstruct_traits & UpdatePixelTrait) == 0))
1348 similarity[j]+=channel_similarity[j];
1349 similarity[CompositePixelChannel]+=channel_similarity[j];
1350 variance[j]+=channel_variance[j];
1351 variance[CompositePixelChannel]+=channel_variance[j];
1352 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1353 reconstruct_variance[CompositePixelChannel]+=
1354 channel_reconstruct_variance[j];
1357 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1362#if defined(MAGICKCORE_OPENMP_SUPPORT)
1366 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1367 if (proceed == MagickFalse)
1374 reconstruct_view=DestroyCacheView(reconstruct_view);
1375 image_view=DestroyCacheView(image_view);
1379 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1381 PixelChannel channel = GetPixelChannelChannel(image,k);
1382 PixelTrait traits = GetPixelChannelTraits(image,channel);
1383 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1385 if (((traits & UpdatePixelTrait) == 0) ||
1386 ((reconstruct_traits & UpdatePixelTrait) == 0))
1388 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1389 sqrt(reconstruct_variance[k]));
1391 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1392 variance[CompositePixelChannel])*sqrt(
1393 reconstruct_variance[CompositePixelChannel]));
1397 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1398 reconstruct_statistics);
1399 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1404static MagickBooleanType GetPASimilarity(
const Image *image,
1405 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1412 status = MagickTrue;
1424 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1425 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1426 image_view=AcquireVirtualCacheView(image,exception);
1427 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1428#if defined(MAGICKCORE_OPENMP_SUPPORT)
1429 #pragma omp parallel for schedule(static) shared(similarity,status) \
1430 magick_number_threads(image,image,rows,1)
1432 for (y=0; y < (ssize_t) rows; y++)
1439 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1444 if (status == MagickFalse)
1446 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1447 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1448 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1453 for (x=0; x < (ssize_t) columns; x++)
1462 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1463 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1465 p+=(ptrdiff_t) GetPixelChannels(image);
1466 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1469 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1470 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1471 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1476 PixelChannel channel = GetPixelChannelChannel(image,i);
1477 PixelTrait traits = GetPixelChannelTraits(image,channel);
1478 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1480 if (((traits & UpdatePixelTrait) == 0) ||
1481 ((reconstruct_traits & UpdatePixelTrait) == 0))
1483 if (channel == AlphaPixelChannel)
1484 distance=QuantumScale*fabs((
double) p[i]-(
double)
1485 GetPixelChannel(reconstruct_image,channel,q));
1487 distance=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(
1488 reconstruct_image,channel,q));
1489 if (distance > channel_similarity[i])
1490 channel_similarity[i]=distance;
1491 if (distance > channel_similarity[CompositePixelChannel])
1492 channel_similarity[CompositePixelChannel]=distance;
1494 p+=(ptrdiff_t) GetPixelChannels(image);
1495 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1497#if defined(MAGICKCORE_OPENMP_SUPPORT)
1498 #pragma omp critical (MagickCore_GetPASimilarity)
1504 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1506 PixelChannel channel = GetPixelChannelChannel(image,j);
1507 PixelTrait traits = GetPixelChannelTraits(image,channel);
1508 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1510 if (((traits & UpdatePixelTrait) == 0) ||
1511 ((reconstruct_traits & UpdatePixelTrait) == 0))
1513 if (channel_similarity[j] > similarity[j])
1514 similarity[j]=channel_similarity[j];
1516 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1517 similarity[CompositePixelChannel]=
1518 channel_similarity[CompositePixelChannel];
1521 reconstruct_view=DestroyCacheView(reconstruct_view);
1522 image_view=DestroyCacheView(image_view);
1526static MagickBooleanType GetPDCSimilarity(
const Image *image,
1527 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1538 status = MagickTrue;
1551 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1552 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1553 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1554 image_view=AcquireVirtualCacheView(image,exception);
1555 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1556#if defined(MMAGICKCORE_OPENMP_SUPPORT)
1557 #pragma omp parallel for schedule(static) shared(similarity,status) \
1558 magick_number_threads(image,image,rows,1)
1560 for (y=0; y < (ssize_t) rows; y++)
1567 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1572 if (status == MagickFalse)
1574 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1575 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1576 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1581 for (x=0; x < (ssize_t) columns; x++)
1593 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1594 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1596 p+=(ptrdiff_t) GetPixelChannels(image);
1597 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1600 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1601 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1602 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1607 PixelChannel channel = GetPixelChannelChannel(image,i);
1608 PixelTrait traits = GetPixelChannelTraits(image,channel);
1609 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1611 if (((traits & UpdatePixelTrait) == 0) ||
1612 ((reconstruct_traits & UpdatePixelTrait) == 0))
1614 if (channel == AlphaPixelChannel)
1615 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1618 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1619 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1621 channel_similarity[i]++;
1626 channel_similarity[CompositePixelChannel]++;
1627 p+=(ptrdiff_t) GetPixelChannels(image);
1628 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1630#if defined(MAGICKCORE_OPENMP_SUPPORT)
1631 #pragma omp critical (MagickCore_GetPDCSimilarity)
1637 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1639 PixelChannel channel = GetPixelChannelChannel(image,j);
1640 PixelTrait traits = GetPixelChannelTraits(image,channel);
1641 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1643 if (((traits & UpdatePixelTrait) == 0) ||
1644 ((reconstruct_traits & UpdatePixelTrait) == 0))
1646 similarity[j]+=channel_similarity[j];
1648 similarity[CompositePixelChannel]+=
1649 channel_similarity[CompositePixelChannel];
1652 reconstruct_view=DestroyCacheView(reconstruct_view);
1653 image_view=DestroyCacheView(image_view);
1654 area=MagickSafeReciprocal((
double) columns*rows);
1655 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1656 similarity[k]*=area;
1657 similarity[CompositePixelChannel]*=area;
1661static MagickBooleanType DFTPhaseSpectrum(
const Image *image,
const ssize_t u,
1662 const ssize_t v,
double *phase,ExceptionInfo *exception)
1664#define PhaseImageTag "Phase/Image"
1670 channel_imag[MaxPixelChannels+1] = { 0.0 },
1671 channel_real[MaxPixelChannels+1] = { 0.0 };
1684 image_view=AcquireVirtualCacheView(image,exception);
1685 for (y=0; y < (ssize_t) image->rows; y++)
1693 if (status == MagickFalse)
1695 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1696 if (p == (
const Quantum *) NULL)
1701 for (x=0; x < (ssize_t) image->columns; x++)
1710 angle=MagickPI*((u*x/(double) image->rows)+(v*y/(double) image->columns));
1711 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1712 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1714 PixelChannel channel = GetPixelChannelChannel(image,i);
1715 PixelTrait traits = GetPixelChannelTraits(image,channel);
1716 if (traits == UndefinedPixelTrait)
1718 if (channel == AlphaPixelChannel)
1720 channel_real[i]+=(QuantumScale*p[i])*cos(angle);
1721 channel_imag[i]-=(QuantumScale*p[i])*sin(angle);
1725 channel_real[i]+=(QuantumScale*Sa*p[i])*cos(angle);
1726 channel_imag[i]-=(QuantumScale*Sa*p[i])*sin(angle);
1729 p+=(ptrdiff_t) GetPixelChannels(image);
1732 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1733 phase[k]=atan2(channel_imag[k],channel_real[k]);
1734 phase[CompositePixelChannel]=atan2(channel_imag[CompositePixelChannel],
1735 channel_real[CompositePixelChannel]);
1736 image_view=DestroyCacheView(image_view);
1740static MagickBooleanType GetPHASESimilarity(
const Image *image,
1741 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1751 status = MagickTrue;
1764 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1765 image_view=AcquireVirtualCacheView(image,exception);
1766 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1767#if defined(MAGICKCORE_OPENMP_SUPPORT)
1768 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1769 magick_number_threads(image,image,rows,1)
1771 for (y=0; y < (ssize_t) rows; y++)
1779 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1784 if (status == MagickFalse)
1786 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1787 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1788 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1793 for (x=0; x < (ssize_t) columns; x++)
1796 phase[MaxPixelChannels+1] = { 0.0 },
1797 reconstruct_phase[MaxPixelChannels+1] = { 0.0 };
1802 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1803 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1805 p+=(ptrdiff_t) GetPixelChannels(image);
1806 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1809 status=DFTPhaseSpectrum(image,x,y,phase,exception);
1810 if (status == MagickFalse)
1812 status=DFTPhaseSpectrum(reconstruct_image,x,y,reconstruct_phase,
1814 if (status == MagickFalse)
1816 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1821 PixelChannel channel = GetPixelChannelChannel(image,i);
1822 PixelTrait traits = GetPixelChannelTraits(image,channel);
1823 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1825 if (((traits & UpdatePixelTrait) == 0) ||
1826 ((reconstruct_traits & UpdatePixelTrait) == 0))
1828 delta=phase[i]-reconstruct_phase[i];
1829 channel_similarity[i]+=cos(delta);
1830 channel_similarity[CompositePixelChannel]+=cos(delta);
1833 p+=(ptrdiff_t) GetPixelChannels(image);
1834 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1836#if defined(MAGICKCORE_OPENMP_SUPPORT)
1837 #pragma omp critical (MagickCore_GetPHASESimilarity)
1844 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1846 PixelChannel channel = GetPixelChannelChannel(image,j);
1847 PixelTrait traits = GetPixelChannelTraits(image,channel);
1848 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1850 if (((traits & UpdatePixelTrait) == 0) ||
1851 ((reconstruct_traits & UpdatePixelTrait) == 0))
1853 similarity[j]+=channel_similarity[j];
1855 similarity[CompositePixelChannel]+=
1856 channel_similarity[CompositePixelChannel];
1859 reconstruct_view=DestroyCacheView(reconstruct_view);
1860 image_view=DestroyCacheView(image_view);
1861 area=MagickSafeReciprocal(area);
1862 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1864 PixelChannel channel = GetPixelChannelChannel(image,k);
1865 PixelTrait traits = GetPixelChannelTraits(image,channel);
1866 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1868 if (((traits & UpdatePixelTrait) == 0) ||
1869 ((reconstruct_traits & UpdatePixelTrait) == 0))
1871 similarity[k]*=area;
1873 similarity[CompositePixelChannel]*=area;
1874 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1878static MagickBooleanType GetPSNRSimilarity(
const Image *image,
1879 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1882 status = MagickTrue;
1890 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
1891 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1893 PixelChannel channel = GetPixelChannelChannel(image,i);
1894 PixelTrait traits = GetPixelChannelTraits(image,channel);
1895 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1897 if (((traits & UpdatePixelTrait) == 0) ||
1898 ((reconstruct_traits & UpdatePixelTrait) == 0))
1900 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
1901 similarity[i]))/MagickSafePSNRRecipicol(10.0);
1903 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
1904 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
1905 MagickSafePSNRRecipicol(10.0);
1909static MagickBooleanType GetPHASHSimilarity(
const Image *image,
1910 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1912 ChannelPerceptualHash
1925 channel_phash=GetImagePerceptualHash(image,exception);
1926 if (channel_phash == (ChannelPerceptualHash *) NULL)
1927 return(MagickFalse);
1928 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
1929 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
1931 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1933 return(MagickFalse);
1935 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1943 PixelChannel channel = GetPixelChannelChannel(image,i);
1944 PixelTrait traits = GetPixelChannelTraits(image,channel);
1945 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1947 if (((traits & UpdatePixelTrait) == 0) ||
1948 ((reconstruct_traits & UpdatePixelTrait) == 0))
1950 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
1959 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
1964 alpha=channel_phash[i].phash[j][k];
1965 beta=reconstruct_phash[i].phash[j][k];
1967 if (IsNaN(error) != 0)
1969 difference+=error*error;
1972 similarity[i]+=difference;
1973 similarity[CompositePixelChannel]+=difference;
1975 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1976 artifact=GetImageArtifact(image,
"phash:normalize");
1977 if (IsStringTrue(artifact) != MagickFalse)
1979 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1981 PixelChannel channel = GetPixelChannelChannel(image,i);
1982 PixelTrait traits = GetPixelChannelTraits(image,channel);
1983 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1985 if (((traits & UpdatePixelTrait) == 0) ||
1986 ((reconstruct_traits & UpdatePixelTrait) == 0))
1988 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
1990 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
1991 channel_phash[0].number_colorspaces);
1996 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1998 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
2002static MagickBooleanType GetRMSESimilarity(
const Image *image,
2003 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2005#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2008 status = MagickTrue;
2016 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2017 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2019 PixelChannel channel = GetPixelChannelChannel(image,i);
2020 PixelTrait traits = GetPixelChannelTraits(image,channel);
2021 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2023 if (((traits & UpdatePixelTrait) == 0) ||
2024 ((reconstruct_traits & UpdatePixelTrait) == 0))
2026 similarity[i]=RMSESquareRoot(similarity[i]);
2028 similarity[CompositePixelChannel]=RMSESquareRoot(
2029 similarity[CompositePixelChannel]);
2033static MagickBooleanType GetSSIMSimularity(
const Image *image,
2034 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2036#define SSIMRadius 5.0
2037#define SSIMSigma 1.5
2047 geometry[MagickPathExtent];
2063 status = MagickTrue;
2077 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2078 if (artifact != (
const char *) NULL)
2079 radius=StringToDouble(artifact,(
char **) NULL);
2081 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2082 if (artifact != (
const char *) NULL)
2083 sigma=StringToDouble(artifact,(
char **) NULL);
2084 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.20gx%.20g",
2086 kernel_info=AcquireKernelInfo(geometry,exception);
2087 if (kernel_info == (KernelInfo *) NULL)
2088 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2090 c1=pow(SSIMK1*SSIML,2.0);
2091 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2092 if (artifact != (
const char *) NULL)
2093 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2094 c2=pow(SSIMK2*SSIML,2.0);
2095 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2096 if (artifact != (
const char *) NULL)
2097 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2098 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2099 image_view=AcquireVirtualCacheView(image,exception);
2100 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2101#if defined(MAGICKCORE_OPENMP_SUPPORT)
2102 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2103 magick_number_threads(image,reconstruct_image,rows,1)
2105 for (y=0; y < (ssize_t) rows; y++)
2113 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2119 if (status == MagickFalse)
2121 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2122 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2123 kernel_info->height,exception);
2124 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2125 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2126 kernel_info->height,exception);
2127 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2132 for (x=0; x < (ssize_t) columns; x++)
2135 *magick_restrict reconstruct,
2136 *magick_restrict test;
2139 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2140 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2141 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2142 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2143 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2151 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2152 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2154 p+=(ptrdiff_t) GetPixelChannels(image);
2155 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2158 k=kernel_info->values;
2161 for (v=0; v < (ssize_t) kernel_info->height; v++)
2166 for (u=0; u < (ssize_t) kernel_info->width; u++)
2168 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2174 PixelChannel channel = GetPixelChannelChannel(image,i);
2175 PixelTrait traits = GetPixelChannelTraits(image,channel);
2176 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2177 reconstruct_image,channel);
2178 if (((traits & UpdatePixelTrait) == 0) ||
2179 ((reconstruct_traits & UpdatePixelTrait) == 0))
2181 x_pixel=QuantumScale*(double) test[i];
2182 x_pixel_mu[i]+=(*k)*x_pixel;
2183 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2184 y_pixel=QuantumScale*(double)
2185 GetPixelChannel(reconstruct_image,channel,reconstruct);
2186 y_pixel_mu[i]+=(*k)*y_pixel;
2187 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2188 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2191 test+=(ptrdiff_t) GetPixelChannels(image);
2192 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2194 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2195 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2197 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2202 x_pixel_sigmas_squared,
2206 y_pixel_sigmas_squared;
2208 PixelChannel channel = GetPixelChannelChannel(image,i);
2209 PixelTrait traits = GetPixelChannelTraits(image,channel);
2210 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2211 reconstruct_image,channel);
2212 if (((traits & UpdatePixelTrait) == 0) ||
2213 ((reconstruct_traits & UpdatePixelTrait) == 0))
2215 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2216 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2217 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2218 xy_sigmas=xy_sigma[i]-xy_mu;
2219 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2220 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2221 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2222 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2223 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2224 channel_similarity[i]+=ssim;
2225 channel_similarity[CompositePixelChannel]+=ssim;
2227 p+=(ptrdiff_t) GetPixelChannels(image);
2228 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2231#if defined(MAGICKCORE_OPENMP_SUPPORT)
2232 #pragma omp critical (MagickCore_GetSSIMSimularity)
2239 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2241 PixelChannel channel = GetPixelChannelChannel(image,j);
2242 PixelTrait traits = GetPixelChannelTraits(image,channel);
2243 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2245 if (((traits & UpdatePixelTrait) == 0) ||
2246 ((reconstruct_traits & UpdatePixelTrait) == 0))
2248 similarity[j]+=channel_similarity[j];
2250 similarity[CompositePixelChannel]+=
2251 channel_similarity[CompositePixelChannel];
2254 image_view=DestroyCacheView(image_view);
2255 reconstruct_view=DestroyCacheView(reconstruct_view);
2256 area=MagickSafeReciprocal(area);
2257 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2259 PixelChannel channel = GetPixelChannelChannel(image,l);
2260 PixelTrait traits = GetPixelChannelTraits(image,channel);
2261 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2263 if (((traits & UpdatePixelTrait) == 0) ||
2264 ((reconstruct_traits & UpdatePixelTrait) == 0))
2266 similarity[l]*=area;
2268 similarity[CompositePixelChannel]*=area;
2269 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2270 kernel_info=DestroyKernelInfo(kernel_info);
2274static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2275 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2278 status = MagickTrue;
2286 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2287 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2289 PixelChannel channel = GetPixelChannelChannel(image,i);
2290 PixelTrait traits = GetPixelChannelTraits(image,channel);
2291 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2293 if (((traits & UpdatePixelTrait) == 0) ||
2294 ((reconstruct_traits & UpdatePixelTrait) == 0))
2296 similarity[i]=(1.0-similarity[i])/2.0;
2298 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2302MagickExport MagickBooleanType GetImageDistortion(Image *image,
2303 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2304 ExceptionInfo *exception)
2306#define CompareMetricNotSupportedException "metric not supported"
2309 *channel_similarity;
2312 status = MagickTrue;
2317 assert(image != (Image *) NULL);
2318 assert(image->signature == MagickCoreSignature);
2319 assert(reconstruct_image != (
const Image *) NULL);
2320 assert(reconstruct_image->signature == MagickCoreSignature);
2321 assert(distortion != (
double *) NULL);
2322 if (IsEventLogging() != MagickFalse)
2323 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2328 length=MaxPixelChannels+1UL;
2329 channel_similarity=(
double *) AcquireQuantumMemory(length,
2330 sizeof(*channel_similarity));
2331 if (channel_similarity == (
double *) NULL)
2332 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2333 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2336 case AbsoluteErrorMetric:
2338 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2342 case DotProductCorrelationErrorMetric:
2344 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2348 case FuzzErrorMetric:
2350 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2354 case MeanAbsoluteErrorMetric:
2356 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2360 case MeanErrorPerPixelErrorMetric:
2362 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2366 case MeanSquaredErrorMetric:
2368 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2372 case NormalizedCrossCorrelationErrorMetric:
2374 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2378 case PeakAbsoluteErrorMetric:
2380 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2384 case PeakSignalToNoiseRatioErrorMetric:
2386 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2390 case PerceptualHashErrorMetric:
2392 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2396 case PhaseCorrelationErrorMetric:
2398 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2402 case PixelDifferenceCountErrorMetric:
2404 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2408 case RootMeanSquaredErrorMetric:
2409 case UndefinedErrorMetric:
2412 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2416 case StructuralDissimilarityErrorMetric:
2418 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2422 case StructuralSimilarityErrorMetric:
2424 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2429 *distortion=channel_similarity[CompositePixelChannel];
2432 case DotProductCorrelationErrorMetric:
2433 case NormalizedCrossCorrelationErrorMetric:
2434 case PhaseCorrelationErrorMetric:
2435 case StructuralSimilarityErrorMetric:
2437 *distortion=(1.0-(*distortion))/2.0;
2442 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2443 if (fabs(*distortion) < MagickEpsilon)
2445 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2481MagickExport
double *GetImageDistortions(Image *image,
2482 const Image *reconstruct_image,
const MetricType metric,
2483 ExceptionInfo *exception)
2487 *channel_similarity;
2490 status = MagickTrue;
2498 assert(image != (Image *) NULL);
2499 assert(image->signature == MagickCoreSignature);
2500 assert(reconstruct_image != (
const Image *) NULL);
2501 assert(reconstruct_image->signature == MagickCoreSignature);
2502 if (IsEventLogging() != MagickFalse)
2503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2507 length=MaxPixelChannels+1UL;
2508 channel_similarity=(
double *) AcquireQuantumMemory(length,
2509 sizeof(*channel_similarity));
2510 if (channel_similarity == (
double *) NULL)
2511 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2512 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2515 case AbsoluteErrorMetric:
2517 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2521 case DotProductCorrelationErrorMetric:
2523 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2527 case FuzzErrorMetric:
2529 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2533 case MeanAbsoluteErrorMetric:
2535 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2539 case MeanErrorPerPixelErrorMetric:
2541 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2545 case MeanSquaredErrorMetric:
2547 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2551 case NormalizedCrossCorrelationErrorMetric:
2553 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2557 case PeakAbsoluteErrorMetric:
2559 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2563 case PeakSignalToNoiseRatioErrorMetric:
2565 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2569 case PerceptualHashErrorMetric:
2571 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2575 case PhaseCorrelationErrorMetric:
2577 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2581 case PixelDifferenceCountErrorMetric:
2583 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2587 case RootMeanSquaredErrorMetric:
2588 case UndefinedErrorMetric:
2591 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2595 case StructuralDissimilarityErrorMetric:
2597 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2601 case StructuralSimilarityErrorMetric:
2603 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2608 if (status == MagickFalse)
2610 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2611 return((
double *) NULL);
2613 distortion=channel_similarity;
2616 case DotProductCorrelationErrorMetric:
2617 case NormalizedCrossCorrelationErrorMetric:
2618 case PhaseCorrelationErrorMetric:
2619 case StructuralSimilarityErrorMetric:
2621 for (i=0; i <= MaxPixelChannels; i++)
2622 distortion[i]=(1.0-distortion[i])/2.0;
2627 for (i=0; i <= MaxPixelChannels; i++)
2628 if (fabs(distortion[i]) < MagickEpsilon)
2630 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2631 distortion[CompositePixelChannel]);
2663MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2664 const Image *reconstruct_image,ExceptionInfo *exception)
2677 assert(image != (Image *) NULL);
2678 assert(image->signature == MagickCoreSignature);
2679 assert(reconstruct_image != (
const Image *) NULL);
2680 assert(reconstruct_image->signature == MagickCoreSignature);
2681 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2682 image_view=AcquireVirtualCacheView(image,exception);
2683 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2684 for (y=0; y < (ssize_t) rows; y++)
2693 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2694 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2695 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2697 for (x=0; x < (ssize_t) columns; x++)
2702 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2707 PixelChannel channel = GetPixelChannelChannel(image,i);
2708 PixelTrait traits = GetPixelChannelTraits(image,channel);
2709 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2711 if (((traits & UpdatePixelTrait) == 0) ||
2712 ((reconstruct_traits & UpdatePixelTrait) == 0))
2714 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2716 if (distance >= MagickEpsilon)
2719 if (i < (ssize_t) GetPixelChannels(image))
2721 p+=(ptrdiff_t) GetPixelChannels(image);
2722 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2724 if (x < (ssize_t) columns)
2727 reconstruct_view=DestroyCacheView(reconstruct_view);
2728 image_view=DestroyCacheView(image_view);
2729 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2781MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2782 const Image *reconstruct_image,ExceptionInfo *exception)
2785 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2790 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2792 if (status == MagickFalse)
2793 return(MagickFalse);
2794 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2795 MagickTrue : MagickFalse;
2842#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2843static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2844 const Image *beta_image,ExceptionInfo *exception)
2847 *alpha_fft = (Image *) NULL,
2848 *beta_fft = (Image *) NULL,
2849 *complex_conjugate = (Image *) NULL,
2850 *complex_multiplication = (Image *) NULL,
2851 *cross_correlation = (Image *) NULL,
2852 *temp_image = (Image *) NULL;
2857 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2858 if (temp_image == (Image *) NULL)
2859 return((Image *) NULL);
2860 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2861 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2862 temp_image=DestroyImageList(temp_image);
2863 if (beta_fft == (Image *) NULL)
2864 return((Image *) NULL);
2868 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2869 beta_fft=DestroyImageList(beta_fft);
2870 if (complex_conjugate == (Image *) NULL)
2871 return((Image *) NULL);
2875 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
2876 if (temp_image == (Image *) NULL)
2878 complex_conjugate=DestroyImageList(complex_conjugate);
2879 return((Image *) NULL);
2881 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2882 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2883 temp_image=DestroyImageList(temp_image);
2884 if (alpha_fft == (Image *) NULL)
2886 complex_conjugate=DestroyImageList(complex_conjugate);
2887 return((Image *) NULL);
2892 DisableCompositeClampUnlessSpecified(complex_conjugate);
2893 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
2894 AppendImageToList(&complex_conjugate,alpha_fft);
2895 complex_multiplication=ComplexImages(complex_conjugate,
2896 MultiplyComplexOperator,exception);
2897 complex_conjugate=DestroyImageList(complex_conjugate);
2898 if (complex_multiplication == (Image *) NULL)
2899 return((Image *) NULL);
2903 cross_correlation=InverseFourierTransformImage(complex_multiplication,
2904 complex_multiplication->next,MagickFalse,exception);
2905 complex_multiplication=DestroyImageList(complex_multiplication);
2906 return(cross_correlation);
2909static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
2910 ExceptionInfo *exception)
2918 kernel_info=AcquireKernelInfo(kernel,exception);
2919 if (kernel_info == (KernelInfo *) NULL)
2920 return((Image *) NULL);
2921 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
2923 kernel_info=DestroyKernelInfo(kernel_info);
2924 return(derivative_image);
2927static Image *SIMDivideImage(
const Image *numerator_image,
2928 const Image *denominator_image,ExceptionInfo *exception)
2938 status = MagickTrue;
2946 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
2947 if (divide_image == (Image *) NULL)
2948 return(divide_image);
2949 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
2950 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
2951#if defined(MAGICKCORE_OPENMP_SUPPORT)
2952 #pragma omp parallel for schedule(static) shared(status) \
2953 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
2955 for (y=0; y < (ssize_t) divide_image->rows; y++)
2966 if (status == MagickFalse)
2968 p=GetCacheViewVirtualPixels(denominator_view,0,y,
2969 denominator_image->columns,1,exception);
2970 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
2972 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2977 for (x=0; x < (ssize_t) divide_image->columns; x++)
2982 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
2984 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
2985 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
2986 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
2988 if (((traits & UpdatePixelTrait) == 0) ||
2989 ((denominator_traits & UpdatePixelTrait) == 0))
2991 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
2992 (
double) GetPixelChannel(denominator_image,channel,p)));
2994 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
2995 q+=(ptrdiff_t) GetPixelChannels(divide_image);
2997 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
3000 denominator_view=DestroyCacheView(denominator_view);
3001 numerator_view=DestroyCacheView(numerator_view);
3002 if (status == MagickFalse)
3003 divide_image=DestroyImage(divide_image);
3004 return(divide_image);
3007static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3008 const Image *source_image,ExceptionInfo *exception)
3017 divide_image=SIMDivideImage(image,magnitude_image,exception);
3018 if (divide_image == (Image *) NULL)
3019 return((Image *) NULL);
3020 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3021 ÷_image->background_color);
3022 SetGeometry(source_image,&geometry);
3023 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3024 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3025 result_image=ExtentImage(divide_image,&geometry,exception);
3026 divide_image=DestroyImage(divide_image);
3027 return(result_image);
3030static MagickBooleanType SIMFilterImageNaNs(Image *image,
3031 ExceptionInfo *exception)
3037 status = MagickTrue;
3045 image_view=AcquireAuthenticCacheView(image,exception);
3046#if defined(MAGICKCORE_OPENMP_SUPPORT)
3047 #pragma omp parallel for schedule(static) shared(status) \
3048 magick_number_threads(image,image,image->rows,1)
3050 for (y=0; y < (ssize_t) image->rows; y++)
3058 if (status == MagickFalse)
3060 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3061 if (q == (Quantum *) NULL)
3066 for (x=0; x < (ssize_t) image->columns; x++)
3071 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3073 PixelChannel channel = GetPixelChannelChannel(image,i);
3074 PixelTrait traits = GetPixelChannelTraits(image,channel);
3075 if ((traits & UpdatePixelTrait) == 0)
3077 if (IsNaN((
double) q[i]) != 0)
3080 q+=(ptrdiff_t) GetPixelChannels(image);
3082 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3085 image_view=DestroyCacheView(image_view);
3089static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3098 status = MagickTrue;
3106 square_image=CloneImage(image,0,0,MagickTrue,exception);
3107 if (square_image == (Image *) NULL)
3108 return(square_image);
3109 image_view=AcquireAuthenticCacheView(square_image,exception);
3110#if defined(MAGICKCORE_OPENMP_SUPPORT)
3111 #pragma omp parallel for schedule(static) shared(status) \
3112 magick_number_threads(square_image,square_image,square_image->rows,1)
3114 for (y=0; y < (ssize_t) square_image->rows; y++)
3122 if (status == MagickFalse)
3124 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3126 if (q == (Quantum *) NULL)
3131 for (x=0; x < (ssize_t) square_image->columns; x++)
3136 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3138 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3139 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3140 if ((traits & UpdatePixelTrait) == 0)
3142 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3144 q+=(ptrdiff_t) GetPixelChannels(square_image);
3146 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3149 image_view=DestroyCacheView(image_view);
3150 if (status == MagickFalse)
3151 square_image=DestroyImage(square_image);
3152 return(square_image);
3155static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3156 ExceptionInfo *exception)
3164 status = MagickTrue;
3166 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3167 xsq_image=SIMSquareImage(alpha_image,exception);
3168 if (xsq_image == (Image *) NULL)
3169 return((Image *) NULL);
3170 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3171 ysq_image=SIMSquareImage(beta_image,exception);
3172 if (ysq_image == (Image *) NULL)
3174 xsq_image=DestroyImage(xsq_image);
3175 return((Image *) NULL);
3177 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3179 magnitude_image=xsq_image;
3180 ysq_image=DestroyImage(ysq_image);
3181 if (status == MagickFalse)
3183 magnitude_image=DestroyImage(magnitude_image);
3184 return((Image *) NULL);
3186 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3187 if (status == MagickFalse)
3189 magnitude_image=DestroyImage(magnitude_image);
3190 return (Image *) NULL;
3192 return(magnitude_image);
3195static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3196 RectangleInfo *offset,ExceptionInfo *exception)
3215 status = MagickTrue;
3218 maxima_info = { -MagickMaximumValue, 0, 0 };
3226 image_view=AcquireVirtualCacheView(image,exception);
3227 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3229 if (q != (
const Quantum *) NULL)
3230 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3231#if defined(MAGICKCORE_OPENMP_SUPPORT)
3232 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3233 magick_number_threads(image,image,image->rows,1)
3235 for (y=0; y < (ssize_t) image->rows; y++)
3241 channel_maxima = { -MagickMaximumValue, 0, 0 };
3246 if (status == MagickFalse)
3248 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3249 if (p == (
const Quantum *) NULL)
3254 channel_maxima=maxima_info;
3255 for (x=0; x < (ssize_t) image->columns; x++)
3260 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3265 PixelChannel channel = GetPixelChannelChannel(image,i);
3266 PixelTrait traits = GetPixelChannelTraits(image,channel);
3267 if ((traits & UpdatePixelTrait) == 0)
3269 pixel=(double) p[i];
3270 if (IsNaN(pixel) != 0)
3272 if (pixel > channel_maxima.maxima)
3274 channel_maxima.maxima=(double) p[i];
3279 p+=(ptrdiff_t) GetPixelChannels(image);
3281#if defined(MAGICKCORE_OPENMP_SUPPORT)
3282 #pragma omp critical (MagickCore_SIMMaximaImage)
3284 if (channel_maxima.maxima > maxima_info.maxima)
3285 maxima_info=channel_maxima;
3287 image_view=DestroyCacheView(image_view);
3288 *maxima=maxima_info.maxima;
3289 offset->x=maxima_info.x;
3290 offset->y=maxima_info.y;
3294static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3295 RectangleInfo *offset,ExceptionInfo *exception)
3314 status = MagickTrue;
3317 minima_info = { MagickMaximumValue, 0, 0 };
3325 image_view=AcquireVirtualCacheView(image,exception);
3326 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3328 if (q != (
const Quantum *) NULL)
3329 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3330#if defined(MAGICKCORE_OPENMP_SUPPORT)
3331 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3332 magick_number_threads(image,image,image->rows,1)
3334 for (y=0; y < (ssize_t) image->rows; y++)
3340 channel_minima = { MagickMaximumValue, 0, 0 };
3345 if (status == MagickFalse)
3347 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3348 if (p == (
const Quantum *) NULL)
3353 channel_minima=minima_info;
3354 for (x=0; x < (ssize_t) image->columns; x++)
3359 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3364 PixelChannel channel = GetPixelChannelChannel(image,i);
3365 PixelTrait traits = GetPixelChannelTraits(image,channel);
3366 if ((traits & UpdatePixelTrait) == 0)
3368 pixel=(double) p[i];
3369 if (IsNaN(pixel) != 0)
3371 if (pixel < channel_minima.minima)
3373 channel_minima.minima=pixel;
3378 p+=(ptrdiff_t) GetPixelChannels(image);
3380#if defined(MAGICKCORE_OPENMP_SUPPORT)
3381 #pragma omp critical (MagickCore_SIMMinimaImage)
3383 if (channel_minima.minima < minima_info.minima)
3384 minima_info=channel_minima;
3386 image_view=DestroyCacheView(image_view);
3387 *minima=minima_info.minima;
3388 offset->x=minima_info.x;
3389 offset->y=minima_info.y;
3393static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3394 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3400 status = MagickTrue;
3408 image_view=AcquireAuthenticCacheView(image,exception);
3409#if defined(MAGICKCORE_OPENMP_SUPPORT)
3410 #pragma omp parallel for schedule(static) shared(status) \
3411 magick_number_threads(image,image,image->rows,1)
3413 for (y=0; y < (ssize_t) image->rows; y++)
3421 if (status == MagickFalse)
3423 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3424 if (q == (Quantum *) NULL)
3429 for (x=0; x < (ssize_t) image->columns; x++)
3434 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3436 PixelChannel channel = GetPixelChannelChannel(image,i);
3437 PixelTrait traits = GetPixelChannelTraits(image,channel);
3438 if ((traits & UpdatePixelTrait) == 0)
3440 if (channel_statistics != (
const ChannelStatistics *) NULL)
3441 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3442 channel_statistics[channel].standard_deviation);
3444 q[i]=(Quantum) (factor*q[i]);
3446 q+=(ptrdiff_t) GetPixelChannels(image);
3448 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3451 image_view=DestroyCacheView(image_view);
3455static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3456 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3459 *alpha_fft = (Image *) NULL,
3460 *beta_fft = (Image *) NULL,
3461 *complex_multiplication = (Image *) NULL,
3462 *cross_correlation = (Image *) NULL;
3467 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3468 if (beta_fft == NULL)
3469 return((Image *) NULL);
3470 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3471 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3472 if (beta_fft == NULL)
3473 return((Image *) NULL);
3477 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3478 if (alpha_fft == (Image *) NULL)
3480 beta_fft=DestroyImageList(beta_fft);
3481 return((Image *) NULL);
3483 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3484 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3485 if (alpha_fft == (Image *) NULL)
3487 beta_fft=DestroyImageList(beta_fft);
3488 return((Image *) NULL);
3493 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3494 if (beta_fft == (Image *) NULL)
3496 alpha_fft=DestroyImageList(alpha_fft);
3497 return((Image *) NULL);
3502 AppendImageToList(&beta_fft,alpha_fft);
3503 DisableCompositeClampUnlessSpecified(beta_fft);
3504 DisableCompositeClampUnlessSpecified(beta_fft->next);
3505 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3507 beta_fft=DestroyImageList(beta_fft);
3508 if (complex_multiplication == (Image *) NULL)
3509 return((Image *) NULL);
3513 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3514 magnitude_image,0,0,exception);
3518 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3519 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3520 complex_multiplication->next,MagickFalse,exception);
3521 complex_multiplication=DestroyImageList(complex_multiplication);
3522 return(cross_correlation);
3525static MagickBooleanType SIMSetImageMean(Image *image,
3526 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3532 status = MagickTrue;
3540 image_view=AcquireAuthenticCacheView(image,exception);
3541#if defined(MAGICKCORE_OPENMP_SUPPORT)
3542 #pragma omp parallel for schedule(static) shared(status) \
3543 magick_number_threads(image,image,image->rows,1)
3545 for (y=0; y < (ssize_t) image->rows; y++)
3553 if (status == MagickFalse)
3555 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3556 if (q == (Quantum *) NULL)
3561 for (x=0; x < (ssize_t) image->columns; x++)
3566 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3568 PixelChannel channel = GetPixelChannelChannel(image,i);
3569 PixelTrait traits = GetPixelChannelTraits(image,channel);
3570 if ((traits & UpdatePixelTrait) == 0)
3572 q[i]=(Quantum) channel_statistics[channel].mean;
3574 q+=(ptrdiff_t) GetPixelChannels(image);
3576 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3579 image_view=DestroyCacheView(image_view);
3583static Image *SIMSubtractImageMean(
const Image *alpha_image,
3584 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3585 ExceptionInfo *exception)
3595 status = MagickTrue;
3603 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3604 MagickTrue,exception);
3605 if (subtract_image == (Image *) NULL)
3606 return(subtract_image);
3607 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3608 beta_view=AcquireVirtualCacheView(beta_image,exception);
3609#if defined(MAGICKCORE_OPENMP_SUPPORT)
3610 #pragma omp parallel for schedule(static) shared(status) \
3611 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3613 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3624 if (status == MagickFalse)
3626 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3627 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3629 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3634 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3639 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3641 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3642 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3643 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3644 if (((traits & UpdatePixelTrait) == 0) ||
3645 ((beta_traits & UpdatePixelTrait) == 0))
3647 if ((x >= (ssize_t) beta_image->columns) ||
3648 (y >= (ssize_t) beta_image->rows))
3651 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3652 channel_statistics[channel].mean);
3654 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3655 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3657 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3660 beta_view=DestroyCacheView(beta_view);
3661 image_view=DestroyCacheView(image_view);
3662 if (status == MagickFalse)
3663 subtract_image=DestroyImage(subtract_image);
3664 return(subtract_image);
3667static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3668 ExceptionInfo *exception)
3677 status = MagickTrue;
3685 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3686 MagickTrue,exception);
3687 if (unity_image == (Image *) NULL)
3688 return(unity_image);
3689 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3690 return(DestroyImage(unity_image));
3691 image_view=AcquireAuthenticCacheView(unity_image,exception);
3692#if defined(MAGICKCORE_OPENMP_SUPPORT)
3693 #pragma omp parallel for schedule(static) shared(status) \
3694 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3696 for (y=0; y < (ssize_t) unity_image->rows; y++)
3704 if (status == MagickFalse)
3706 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3708 if (q == (Quantum *) NULL)
3713 for (x=0; x < (ssize_t) unity_image->columns; x++)
3718 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3720 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3721 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3722 if ((traits & UpdatePixelTrait) == 0)
3724 if ((x >= (ssize_t) beta_image->columns) ||
3725 (y >= (ssize_t) beta_image->rows))
3730 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3732 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3735 image_view=DestroyCacheView(image_view);
3736 if (status == MagickFalse)
3737 unity_image=DestroyImage(unity_image);
3738 return(unity_image);
3741static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3742 ExceptionInfo *exception)
3752 status = MagickTrue;
3760 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3761 if (variance_image == (Image *) NULL)
3762 return(variance_image);
3763 image_view=AcquireAuthenticCacheView(variance_image,exception);
3764 beta_view=AcquireVirtualCacheView(beta_image,exception);
3765#if defined(MAGICKCORE_OPENMP_SUPPORT)
3766 #pragma omp parallel for schedule(static) shared(status) \
3767 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3769 for (y=0; y < (ssize_t) variance_image->rows; y++)
3780 if (status == MagickFalse)
3782 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3784 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3786 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3791 for (x=0; x < (ssize_t) variance_image->columns; x++)
3796 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3801 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3802 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3803 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3804 if (((traits & UpdatePixelTrait) == 0) ||
3805 ((beta_traits & UpdatePixelTrait) == 0))
3807 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3808 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3809 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3811 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3812 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3814 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3817 beta_view=DestroyCacheView(beta_view);
3818 image_view=DestroyCacheView(image_view);
3819 if (status == MagickFalse)
3820 variance_image=DestroyImage(variance_image);
3821 return(variance_image);
3824static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3825 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3827#define ThrowDPCSimilarityException() \
3829 if (dot_product_image != (Image *) NULL) \
3830 dot_product_image=DestroyImage(dot_product_image); \
3831 if (magnitude_image != (Image *) NULL) \
3832 magnitude_image=DestroyImage(magnitude_image); \
3833 if (reconstruct_image != (Image *) NULL) \
3834 reconstruct_image=DestroyImage(reconstruct_image); \
3835 if (rx_image != (Image *) NULL) \
3836 rx_image=DestroyImage(rx_image); \
3837 if (ry_image != (Image *) NULL) \
3838 ry_image=DestroyImage(ry_image); \
3839 if (target_image != (Image *) NULL) \
3840 target_image=DestroyImage(target_image); \
3841 if (threshold_image != (Image *) NULL) \
3842 threshold_image=DestroyImage(threshold_image); \
3843 if (trx_image != (Image *) NULL) \
3844 trx_image=DestroyImage(trx_image); \
3845 if (try_image != (Image *) NULL) \
3846 try_image=DestroyImage(try_image); \
3847 if (tx_image != (Image *) NULL) \
3848 tx_image=DestroyImage(tx_image); \
3849 if (ty_image != (Image *) NULL) \
3850 ty_image=DestroyImage(ty_image); \
3851 return((Image *) NULL); \
3858 standard_deviation = 0.0;
3861 *dot_product_image = (Image *) NULL,
3862 *magnitude_image = (Image *) NULL,
3863 *reconstruct_image = (Image *) NULL,
3864 *rx_image = (Image *) NULL,
3865 *ry_image = (Image *) NULL,
3866 *trx_image = (Image *) NULL,
3867 *target_image = (Image *) NULL,
3868 *threshold_image = (Image *) NULL,
3869 *try_image = (Image *) NULL,
3870 *tx_image = (Image *) NULL,
3871 *ty_image = (Image *) NULL;
3874 status = MagickTrue;
3882 target_image=CloneImage(image,0,0,MagickTrue,exception);
3883 if (target_image == (Image *) NULL)
3884 return((Image *) NULL);
3888 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
3889 if (reconstruct_image == (Image *) NULL)
3890 ThrowDPCSimilarityException();
3894 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
3896 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
3897 if (rx_image == (Image *) NULL)
3898 ThrowDPCSimilarityException();
3899 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
3900 reconstruct_image=DestroyImage(reconstruct_image);
3901 if (ry_image == (Image *) NULL)
3902 ThrowDPCSimilarityException();
3906 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
3907 if (magnitude_image == (Image *) NULL)
3908 ThrowDPCSimilarityException();
3912 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
3913 if (threshold_image == (Image *) NULL)
3914 ThrowDPCSimilarityException();
3915 status=BilevelImage(threshold_image,0.0,exception);
3916 if (status == MagickFalse)
3917 ThrowDPCSimilarityException();
3918 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
3919 threshold_image=DestroyImage(threshold_image);
3920 if (status == MagickFalse)
3921 ThrowDPCSimilarityException();
3922 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
3923 reconstruct->rows)+QuantumScale;
3927 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
3928 rx_image=DestroyImage(rx_image);
3929 if (trx_image == (Image *) NULL)
3930 ThrowDPCSimilarityException();
3932 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
3933 magnitude_image=DestroyImage(magnitude_image);
3934 ry_image=DestroyImage(ry_image);
3935 if (try_image == (Image *) NULL)
3936 ThrowDPCSimilarityException();
3941 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
3943 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
3944 if (tx_image == (Image *) NULL)
3945 ThrowDPCSimilarityException();
3946 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
3947 target_image=DestroyImage(target_image);
3948 if (ty_image == (Image *) NULL)
3949 ThrowDPCSimilarityException();
3953 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
3954 if (magnitude_image == (Image *) NULL)
3955 ThrowDPCSimilarityException();
3959 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
3960 tx_image=DestroyImage(tx_image);
3961 if (trx_image == (Image *) NULL)
3962 ThrowDPCSimilarityException();
3964 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
3965 ty_image=DestroyImage(ty_image);
3966 magnitude_image=DestroyImage(magnitude_image);
3967 if (try_image == (Image *) NULL)
3968 ThrowDPCSimilarityException();
3973 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
3974 rx_image=DestroyImage(rx_image);
3975 tx_image=DestroyImage(tx_image);
3976 if (trx_image == (Image *) NULL)
3977 ThrowDPCSimilarityException();
3978 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
3979 ry_image=DestroyImage(ry_image);
3980 ty_image=DestroyImage(ty_image);
3981 if (try_image == (Image *) NULL)
3982 ThrowDPCSimilarityException();
3986 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
3987 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
3989 try_image=DestroyImage(try_image);
3990 if (status == MagickFalse)
3991 ThrowDPCSimilarityException();
3992 status=SIMMultiplyImage(trx_image,edge_factor,
3993 (
const ChannelStatistics *) NULL,exception);
3994 if (status == MagickFalse)
3995 ThrowDPCSimilarityException();
3999 SetGeometry(image,&geometry);
4000 geometry.width=image->columns;
4001 geometry.height=image->rows;
4002 (void) ResetImagePage(trx_image,
"0x0+0+0");
4003 dot_product_image=CropImage(trx_image,&geometry,exception);
4004 trx_image=DestroyImage(trx_image);
4005 if (dot_product_image == (Image *) NULL)
4006 ThrowDPCSimilarityException();
4007 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4011 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4013 if (status == MagickFalse)
4014 ThrowDPCSimilarityException();
4015 dot_product_image->depth=32;
4016 dot_product_image->colorspace=GRAYColorspace;
4017 dot_product_image->alpha_trait=UndefinedPixelTrait;
4018 status=SIMFilterImageNaNs(dot_product_image,exception);
4019 if (status == MagickFalse)
4020 ThrowDPCSimilarityException();
4021 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4022 if (status == MagickFalse)
4023 ThrowDPCSimilarityException();
4024 if ((QuantumScale*maxima) > 1.0)
4026 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4027 (
const ChannelStatistics *) NULL,exception);
4028 maxima=(double) QuantumRange;
4030 *similarity_metric=QuantumScale*maxima;
4031 return(dot_product_image);
4034static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4035 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4037#define ThrowMSESimilarityException() \
4039 if (alpha_image != (Image *) NULL) \
4040 alpha_image=DestroyImage(alpha_image); \
4041 if (beta_image != (Image *) NULL) \
4042 beta_image=DestroyImage(beta_image); \
4043 if (channel_statistics != (ChannelStatistics *) NULL) \
4044 channel_statistics=(ChannelStatistics *) \
4045 RelinquishMagickMemory(channel_statistics); \
4046 if (mean_image != (Image *) NULL) \
4047 mean_image=DestroyImage(mean_image); \
4048 if (mse_image != (Image *) NULL) \
4049 mse_image=DestroyImage(mse_image); \
4050 if (reconstruct_image != (Image *) NULL) \
4051 reconstruct_image=DestroyImage(reconstruct_image); \
4052 if (sum_image != (Image *) NULL) \
4053 sum_image=DestroyImage(sum_image); \
4054 if (alpha_image != (Image *) NULL) \
4055 alpha_image=DestroyImage(alpha_image); \
4056 return((Image *) NULL); \
4060 *channel_statistics = (ChannelStatistics *) NULL;
4066 *alpha_image = (Image *) NULL,
4067 *beta_image = (Image *) NULL,
4068 *mean_image = (Image *) NULL,
4069 *mse_image = (Image *) NULL,
4070 *reconstruct_image = (Image *) NULL,
4071 *sum_image = (Image *) NULL,
4072 *target_image = (Image *) NULL;
4075 status = MagickTrue;
4083 target_image=SIMSquareImage(image,exception);
4084 if (target_image == (Image *) NULL)
4085 ThrowMSESimilarityException();
4086 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4087 if (reconstruct_image == (Image *) NULL)
4088 ThrowMSESimilarityException();
4092 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4094 target_image=DestroyImage(target_image);
4095 if (alpha_image == (Image *) NULL)
4096 ThrowMSESimilarityException();
4097 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4098 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4099 if (status == MagickFalse)
4100 ThrowMSESimilarityException();
4104 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4105 MagickTrue,0,0,exception);
4106 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4107 reconstruct_image=DestroyImage(reconstruct_image);
4108 if (beta_image == (Image *) NULL)
4109 ThrowMSESimilarityException();
4110 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4111 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4112 if (status == MagickFalse)
4113 ThrowMSESimilarityException();
4117 sum_image=SIMSquareImage(reconstruct,exception);
4118 if (sum_image == (Image *) NULL)
4119 ThrowMSESimilarityException();
4120 channel_statistics=GetImageStatistics(sum_image,exception);
4121 if (channel_statistics == (ChannelStatistics *) NULL)
4122 ThrowMSESimilarityException();
4123 status=SetImageStorageClass(sum_image,DirectClass,exception);
4124 if (status == MagickFalse)
4125 ThrowMSESimilarityException();
4126 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4127 channel_statistics=(ChannelStatistics *)
4128 RelinquishMagickMemory(channel_statistics);
4129 if (status == MagickFalse)
4130 ThrowMSESimilarityException();
4134 AppendImageToList(&sum_image,alpha_image);
4135 AppendImageToList(&sum_image,beta_image);
4136 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4137 if (mean_image == (Image *) NULL)
4138 ThrowMSESimilarityException();
4139 sum_image=DestroyImage(sum_image);
4140 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4141 if (status == MagickFalse)
4142 ThrowMSESimilarityException();
4146 SetGeometry(image,&geometry);
4147 geometry.width=image->columns;
4148 geometry.height=image->rows;
4149 (void) ResetImagePage(mean_image,
"0x0+0+0");
4150 mse_image=CropImage(mean_image,&geometry,exception);
4151 mean_image=DestroyImage(mean_image);
4152 if (mse_image == (Image *) NULL)
4153 ThrowMSESimilarityException();
4157 (void) ResetImagePage(mse_image,
"0x0+0+0");
4158 (void) ClampImage(mse_image,exception);
4159 mse_image->depth=32;
4160 mse_image->colorspace=GRAYColorspace;
4161 mse_image->alpha_trait=UndefinedPixelTrait;
4162 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4163 if (status == MagickFalse)
4164 ThrowMSESimilarityException();
4165 status=NegateImage(mse_image,MagickFalse,exception);
4166 if (status == MagickFalse)
4167 ThrowMSESimilarityException();
4168 alpha_image=DestroyImage(alpha_image);
4169 beta_image=DestroyImage(beta_image);
4170 if ((QuantumScale*minima) < FLT_EPSILON)
4172 *similarity_metric=QuantumScale*minima;
4176static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4177 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4179#define ThrowNCCSimilarityException() \
4181 if (alpha_image != (Image *) NULL) \
4182 alpha_image=DestroyImage(alpha_image); \
4183 if (beta_image != (Image *) NULL) \
4184 beta_image=DestroyImage(beta_image); \
4185 if (channel_statistics != (ChannelStatistics *) NULL) \
4186 channel_statistics=(ChannelStatistics *) \
4187 RelinquishMagickMemory(channel_statistics); \
4188 if (correlation_image != (Image *) NULL) \
4189 correlation_image=DestroyImage(correlation_image); \
4190 if (divide_image != (Image *) NULL) \
4191 divide_image=DestroyImage(divide_image); \
4192 if (ncc_image != (Image *) NULL) \
4193 ncc_image=DestroyImage(ncc_image); \
4194 if (normalize_image != (Image *) NULL) \
4195 normalize_image=DestroyImage(normalize_image); \
4196 if (reconstruct_image != (Image *) NULL) \
4197 reconstruct_image=DestroyImage(reconstruct_image); \
4198 if (target_image != (Image *) NULL) \
4199 target_image=DestroyImage(target_image); \
4200 if (variance_image != (Image *) NULL) \
4201 variance_image=DestroyImage(variance_image); \
4202 return((Image *) NULL); \
4206 *channel_statistics = (ChannelStatistics *) NULL;
4212 *alpha_image = (Image *) NULL,
4213 *beta_image = (Image *) NULL,
4214 *correlation_image = (Image *) NULL,
4215 *divide_image = (Image *) NULL,
4216 *ncc_image = (Image *) NULL,
4217 *normalize_image = (Image *) NULL,
4218 *reconstruct_image = (Image *) NULL,
4219 *target_image = (Image *) NULL,
4220 *variance_image = (Image *) NULL;
4223 status = MagickTrue;
4231 target_image=SIMSquareImage(image,exception);
4232 if (target_image == (Image *) NULL)
4233 ThrowNCCSimilarityException();
4234 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4235 if (reconstruct_image == (Image *) NULL)
4236 ThrowNCCSimilarityException();
4240 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4242 target_image=DestroyImage(target_image);
4243 if (alpha_image == (Image *) NULL)
4244 ThrowNCCSimilarityException();
4245 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4246 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4248 if (status == MagickFalse)
4249 ThrowNCCSimilarityException();
4253 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4254 reconstruct_image=DestroyImage(reconstruct_image);
4255 if (beta_image == (Image *) NULL)
4256 ThrowNCCSimilarityException();
4257 target_image=SIMSquareImage(beta_image,exception);
4258 beta_image=DestroyImage(beta_image);
4259 if (target_image == (Image *) NULL)
4260 ThrowNCCSimilarityException();
4261 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4262 (
const ChannelStatistics *) NULL,exception);
4263 if (status == MagickFalse)
4264 ThrowNCCSimilarityException();
4268 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4269 target_image=DestroyImage(target_image);
4270 alpha_image=DestroyImage(alpha_image);
4271 if (variance_image == (Image *) NULL)
4272 ThrowNCCSimilarityException();
4276 channel_statistics=GetImageStatistics(reconstruct,exception);
4277 if (channel_statistics == (ChannelStatistics *) NULL)
4278 ThrowNCCSimilarityException();
4279 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4280 if (status == MagickFalse)
4281 ThrowNCCSimilarityException();
4282 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4284 channel_statistics=(ChannelStatistics *)
4285 RelinquishMagickMemory(channel_statistics);
4286 if (normalize_image == (Image *) NULL)
4287 ThrowNCCSimilarityException();
4288 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4289 normalize_image=DestroyImage(normalize_image);
4290 if (correlation_image == (Image *) NULL)
4291 ThrowNCCSimilarityException();
4295 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4296 correlation_image=DestroyImage(correlation_image);
4297 variance_image=DestroyImage(variance_image);
4298 if (divide_image == (Image *) NULL)
4299 ThrowNCCSimilarityException();
4303 SetGeometry(image,&geometry);
4304 geometry.width=image->columns;
4305 geometry.height=image->rows;
4306 (void) ResetImagePage(divide_image,
"0x0+0+0");
4307 ncc_image=CropImage(divide_image,&geometry,exception);
4308 divide_image=DestroyImage(divide_image);
4309 if (ncc_image == (Image *) NULL)
4310 ThrowNCCSimilarityException();
4314 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4315 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4316 if (status == MagickFalse)
4317 ThrowNCCSimilarityException();
4318 ncc_image->depth=32;
4319 ncc_image->colorspace=GRAYColorspace;
4320 ncc_image->alpha_trait=UndefinedPixelTrait;
4321 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4322 if (status == MagickFalse)
4323 ThrowNCCSimilarityException();
4324 if ((QuantumScale*maxima) > 1.0)
4326 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4327 (
const ChannelStatistics *) NULL,exception);
4328 maxima=(double) QuantumRange;
4330 *similarity_metric=QuantumScale*maxima;
4334static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4335 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4337#define ThrowPhaseSimilarityException() \
4339 if (correlation_image != (Image *) NULL) \
4340 correlation_image=DestroyImage(correlation_image); \
4341 if (fft_images != (Image *) NULL) \
4342 fft_images=DestroyImageList(fft_images); \
4343 if (gamma_image != (Image *) NULL) \
4344 gamma_image=DestroyImage(gamma_image); \
4345 if (magnitude_image != (Image *) NULL) \
4346 magnitude_image=DestroyImage(magnitude_image); \
4347 if (phase_image != (Image *) NULL) \
4348 phase_image=DestroyImage(phase_image); \
4349 if (reconstruct_image != (Image *) NULL) \
4350 reconstruct_image=DestroyImage(reconstruct_image); \
4351 if (reconstruct_magnitude != (Image *) NULL) \
4352 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4353 if (target_image != (Image *) NULL) \
4354 target_image=DestroyImage(target_image); \
4355 if (test_magnitude != (Image *) NULL) \
4356 test_magnitude=DestroyImage(test_magnitude); \
4357 return((Image *) NULL); \
4364 *correlation_image = (Image *) NULL,
4365 *fft_images = (Image *) NULL,
4366 *gamma_image = (Image *) NULL,
4367 *magnitude_image = (Image *) NULL,
4368 *phase_image = (Image *) NULL,
4369 *reconstruct_image = (Image *) NULL,
4370 *reconstruct_magnitude = (Image *) NULL,
4371 *target_image = (Image *) NULL,
4372 *test_magnitude = (Image *) NULL;
4375 status = MagickTrue;
4383 target_image=CloneImage(image,0,0,MagickTrue,exception);
4384 if (target_image == (Image *) NULL)
4385 ThrowPhaseSimilarityException();
4386 (void) ResetImagePage(target_image,
"0x0+0+0");
4387 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4388 &target_image->background_color);
4389 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4390 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4391 if (status == MagickFalse)
4392 ThrowPhaseSimilarityException();
4396 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4397 if (reconstruct_image == (Image *) NULL)
4398 ThrowPhaseSimilarityException();
4399 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4400 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4401 &reconstruct_image->background_color);
4402 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4403 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4404 if (status == MagickFalse)
4405 ThrowPhaseSimilarityException();
4409 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4410 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4411 if (fft_images == (Image *) NULL)
4412 ThrowPhaseSimilarityException();
4413 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4414 fft_images=DestroyImageList(fft_images);
4415 if (test_magnitude == (Image *) NULL)
4416 ThrowPhaseSimilarityException();
4417 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4418 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4420 if (fft_images == (Image *) NULL)
4421 ThrowPhaseSimilarityException();
4422 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4423 fft_images=DestroyImageList(fft_images);
4424 if (reconstruct_magnitude == (Image *) NULL)
4425 ThrowPhaseSimilarityException();
4426 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4427 if (magnitude_image == (Image *) NULL)
4428 ThrowPhaseSimilarityException();
4429 DisableCompositeClampUnlessSpecified(magnitude_image);
4430 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4431 MagickTrue,0,0,exception);
4435 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4436 magnitude_image,exception);
4437 target_image=DestroyImage(target_image);
4438 reconstruct_image=DestroyImage(reconstruct_image);
4439 test_magnitude=DestroyImage(test_magnitude);
4440 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4441 if (correlation_image == (Image *) NULL)
4442 ThrowPhaseSimilarityException();
4446 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4447 correlation_image=DestroyImage(correlation_image);
4448 if (gamma_image == (Image *) NULL)
4449 ThrowPhaseSimilarityException();
4453 SetGeometry(image,&geometry);
4454 geometry.width=image->columns;
4455 geometry.height=image->rows;
4456 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4457 phase_image=CropImage(gamma_image,&geometry,exception);
4458 gamma_image=DestroyImage(gamma_image);
4459 if (phase_image == (Image *) NULL)
4460 ThrowPhaseSimilarityException();
4461 (void) ResetImagePage(phase_image,
"0x0+0+0");
4465 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4466 if (status == MagickFalse)
4467 ThrowPhaseSimilarityException();
4468 phase_image->depth=32;
4469 phase_image->colorspace=GRAYColorspace;
4470 phase_image->alpha_trait=UndefinedPixelTrait;
4471 status=SIMFilterImageNaNs(phase_image,exception);
4472 if (status == MagickFalse)
4473 ThrowPhaseSimilarityException();
4474 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4475 if (status == MagickFalse)
4476 ThrowPhaseSimilarityException();
4477 magnitude_image=DestroyImage(magnitude_image);
4478 if ((QuantumScale*maxima) > 1.0)
4480 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4481 (
const ChannelStatistics *) NULL,exception);
4482 maxima=(double) QuantumRange;
4484 *similarity_metric=QuantumScale*maxima;
4485 return(phase_image);
4488static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4489 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4492 *psnr_image = (Image *) NULL;
4494 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4496 if (psnr_image == (Image *) NULL)
4498 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4499 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4503static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4504 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4507 *rmse_image = (Image *) NULL;
4509 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4511 if (rmse_image == (Image *) NULL)
4513 *similarity_metric=sqrt(*similarity_metric);
4518static double GetSimilarityMetric(
const Image *image,
4519 const Image *reconstruct_image,
const MetricType metric,
4520 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4523 *channel_similarity,
4527 *sans_exception = AcquireExceptionInfo();
4533 status = MagickTrue;
4539 length = MaxPixelChannels+1UL;
4541 SetGeometry(reconstruct_image,&geometry);
4542 geometry.x=x_offset;
4543 geometry.y=y_offset;
4544 similarity_image=CropImage(image,&geometry,sans_exception);
4545 sans_exception=DestroyExceptionInfo(sans_exception);
4546 if (similarity_image == (Image *) NULL)
4551 channel_similarity=(
double *) AcquireQuantumMemory(length,
4552 sizeof(*channel_similarity));
4553 if (channel_similarity == (
double *) NULL)
4554 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4555 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4558 case AbsoluteErrorMetric:
4560 status=GetAESimilarity(similarity_image,reconstruct_image,
4561 channel_similarity,exception);
4564 case DotProductCorrelationErrorMetric:
4566 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4567 channel_similarity,exception);
4570 case FuzzErrorMetric:
4572 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4573 channel_similarity,exception);
4576 case MeanAbsoluteErrorMetric:
4578 status=GetMAESimilarity(similarity_image,reconstruct_image,
4579 channel_similarity,exception);
4582 case MeanErrorPerPixelErrorMetric:
4584 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4585 channel_similarity,exception);
4588 case MeanSquaredErrorMetric:
4590 status=GetMSESimilarity(similarity_image,reconstruct_image,
4591 channel_similarity,exception);
4594 case NormalizedCrossCorrelationErrorMetric:
4596 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4597 channel_similarity,exception);
4600 case PeakAbsoluteErrorMetric:
4602 status=GetPASimilarity(similarity_image,reconstruct_image,
4603 channel_similarity,exception);
4606 case PeakSignalToNoiseRatioErrorMetric:
4608 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4609 channel_similarity,exception);
4612 case PerceptualHashErrorMetric:
4614 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4615 channel_similarity,exception);
4618 case PhaseCorrelationErrorMetric:
4620 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4621 channel_similarity,exception);
4624 case PixelDifferenceCountErrorMetric:
4626 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4627 channel_similarity,exception);
4630 case RootMeanSquaredErrorMetric:
4631 case UndefinedErrorMetric:
4634 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4635 channel_similarity,exception);
4638 case StructuralDissimilarityErrorMetric:
4640 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4641 channel_similarity,exception);
4644 case StructuralSimilarityErrorMetric:
4646 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4647 channel_similarity,exception);
4651 similarity_image=DestroyImage(similarity_image);
4652 similarity=channel_similarity[CompositePixelChannel];
4653 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4654 if (status == MagickFalse)
4659MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4660 const MetricType metric,
const double similarity_threshold,
4661 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4663#define SimilarityImageTag "Similarity/Image"
4679 *similarity_image = (Image *) NULL;
4682 status = MagickTrue;
4688 similarity_info = { 0.0, 0, 0 };
4693 assert(image != (
const Image *) NULL);
4694 assert(image->signature == MagickCoreSignature);
4695 assert(exception != (ExceptionInfo *) NULL);
4696 assert(exception->signature == MagickCoreSignature);
4697 assert(offset != (RectangleInfo *) NULL);
4698 if (IsEventLogging() != MagickFalse)
4699 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4700 SetGeometry(reconstruct,offset);
4701 *similarity_metric=0.0;
4704#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4706 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4707 if (artifact == (
const char *) NULL)
4708 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4709 if (((artifact == (
const char *) NULL) ||
4710 (IsStringTrue(artifact) != MagickFalse)) &&
4711 ((image->channels & ReadMaskChannel) == 0))
4714 case DotProductCorrelationErrorMetric:
4716 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4717 similarity_metric,exception);
4718 return(similarity_image);
4720 case MeanSquaredErrorMetric:
4722 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4723 similarity_metric,exception);
4724 return(similarity_image);
4726 case NormalizedCrossCorrelationErrorMetric:
4728 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4729 similarity_metric,exception);
4730 return(similarity_image);
4732 case PeakSignalToNoiseRatioErrorMetric:
4734 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4735 similarity_metric,exception);
4736 return(similarity_image);
4738 case PhaseCorrelationErrorMetric:
4740 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4741 similarity_metric,exception);
4742 return(similarity_image);
4744 case RootMeanSquaredErrorMetric:
4745 case UndefinedErrorMetric:
4747 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4748 similarity_metric,exception);
4749 return(similarity_image);
4756 if ((image->columns < reconstruct->columns) ||
4757 (image->rows < reconstruct->rows))
4759 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4760 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4761 return((Image *) NULL);
4763 similarity_image=CloneImage(image,image->columns-reconstruct->columns+1,
4764 image->rows-reconstruct->rows+1,MagickTrue,exception);
4765 if (similarity_image == (Image *) NULL)
4766 return((Image *) NULL);
4767 similarity_image->depth=32;
4768 similarity_image->colorspace=GRAYColorspace;
4769 similarity_image->alpha_trait=UndefinedPixelTrait;
4770 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4771 if (status == MagickFalse)
4772 return(DestroyImage(similarity_image));
4776 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4777 similarity_info.x,similarity_info.y,exception);
4778 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4779#if defined(MAGICKCORE_OPENMP_SUPPORT)
4780 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4781 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4783 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4789 threshold_trigger = MagickFalse;
4795 channel_info = similarity_info;
4800 if (status == MagickFalse)
4802 if (threshold_trigger != MagickFalse)
4804 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4805 similarity_image->columns,1,exception);
4806 if (q == (Quantum *) NULL)
4811 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4816 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4820 case DotProductCorrelationErrorMetric:
4821 case NormalizedCrossCorrelationErrorMetric:
4822 case PeakSignalToNoiseRatioErrorMetric:
4823 case PhaseCorrelationErrorMetric:
4824 case StructuralSimilarityErrorMetric:
4826 if (similarity <= channel_info.similarity)
4828 channel_info.similarity=similarity;
4835 if (similarity >= channel_info.similarity)
4837 channel_info.similarity=similarity;
4843 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4845 PixelChannel channel = GetPixelChannelChannel(image,i);
4846 PixelTrait traits = GetPixelChannelTraits(image,channel);
4847 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4849 if (((traits & UpdatePixelTrait) == 0) ||
4850 ((similarity_traits & UpdatePixelTrait) == 0))
4854 case DotProductCorrelationErrorMetric:
4855 case NormalizedCrossCorrelationErrorMetric:
4856 case PeakSignalToNoiseRatioErrorMetric:
4857 case PhaseCorrelationErrorMetric:
4858 case StructuralSimilarityErrorMetric:
4860 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4861 QuantumRange*similarity),q);
4866 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4867 QuantumRange*(1.0-similarity)),q);
4872 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
4874#if defined(MAGICKCORE_OPENMP_SUPPORT)
4875 #pragma omp critical (MagickCore_GetSimilarityMetric)
4879 case DotProductCorrelationErrorMetric:
4880 case NormalizedCrossCorrelationErrorMetric:
4881 case PeakSignalToNoiseRatioErrorMetric:
4882 case PhaseCorrelationErrorMetric:
4883 case StructuralSimilarityErrorMetric:
4885 if (similarity_threshold != DefaultSimilarityThreshold)
4886 if (channel_info.similarity >= similarity_threshold)
4887 threshold_trigger=MagickTrue;
4888 if (channel_info.similarity >= similarity_info.similarity)
4889 similarity_info=channel_info;
4894 if (similarity_threshold != DefaultSimilarityThreshold)
4895 if (channel_info.similarity < similarity_threshold)
4896 threshold_trigger=MagickTrue;
4897 if (channel_info.similarity < similarity_info.similarity)
4898 similarity_info=channel_info;
4902 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
4904 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4910 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
4911 if (proceed == MagickFalse)
4915 similarity_view=DestroyCacheView(similarity_view);
4916 if (status == MagickFalse)
4917 similarity_image=DestroyImage(similarity_image);
4918 *similarity_metric=similarity_info.similarity;
4919 if (fabs(*similarity_metric) < MagickEpsilon)
4920 *similarity_metric=0.0;
4921 offset->x=similarity_info.x;
4922 offset->y=similarity_info.y;
4923 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
4924 GetMagickPrecision(),*similarity_metric);
4925 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
4926 GetMagickPrecision(),(
double) offset->x);
4927 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
4928 GetMagickPrecision(),(
double) offset->y);
4929 return(similarity_image);