43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/channel.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/compare.h"
54#include "MagickCore/compare-private.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/enhance.h"
60#include "MagickCore/fourier.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/registry.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/statistic.h"
75#include "MagickCore/statistic-private.h"
76#include "MagickCore/string-private.h"
77#include "MagickCore/thread-private.h"
78#include "MagickCore/threshold.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/utility.h"
81#include "MagickCore/version.h"
115MagickExport Image *CompareImages(Image *image,
const Image *reconstruct_image,
116 const MetricType metric,
double *distortion,ExceptionInfo *exception)
149 assert(image != (Image *) NULL);
150 assert(image->signature == MagickCoreSignature);
151 assert(reconstruct_image != (
const Image *) NULL);
152 assert(reconstruct_image->signature == MagickCoreSignature);
153 assert(distortion != (
double *) NULL);
154 if (IsEventLogging() != MagickFalse)
155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
157 status=GetImageDistortion(image,reconstruct_image,metric,distortion,
159 if (status == MagickFalse)
160 return((Image *) NULL);
161 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
162 SetGeometry(image,&geometry);
163 geometry.width=columns;
164 geometry.height=rows;
165 clone_image=CloneImage(image,0,0,MagickTrue,exception);
166 if (clone_image == (Image *) NULL)
167 return((Image *) NULL);
168 (void) SetImageMask(clone_image,ReadPixelMask,(Image *) NULL,exception);
169 difference_image=ExtentImage(clone_image,&geometry,exception);
170 clone_image=DestroyImage(clone_image);
171 if (difference_image == (Image *) NULL)
172 return((Image *) NULL);
173 (void) ResetImagePage(difference_image,
"0x0+0+0");
174 (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel,exception);
175 highlight_image=CloneImage(image,columns,rows,MagickTrue,exception);
176 if (highlight_image == (Image *) NULL)
178 difference_image=DestroyImage(difference_image);
179 return((Image *) NULL);
181 status=SetImageStorageClass(highlight_image,DirectClass,exception);
182 if (status == MagickFalse)
184 difference_image=DestroyImage(difference_image);
185 highlight_image=DestroyImage(highlight_image);
186 return((Image *) NULL);
188 (void) SetImageMask(highlight_image,ReadPixelMask,(Image *) NULL,exception);
189 (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel,exception);
190 (void) QueryColorCompliance(
"#f1001ecc",AllCompliance,&highlight,exception);
191 artifact=GetImageArtifact(image,
"compare:highlight-color");
192 if (artifact != (
const char *) NULL)
193 (void) QueryColorCompliance(artifact,AllCompliance,&highlight,exception);
194 (void) QueryColorCompliance(
"#ffffffcc",AllCompliance,&lowlight,exception);
195 artifact=GetImageArtifact(image,
"compare:lowlight-color");
196 if (artifact != (
const char *) NULL)
197 (void) QueryColorCompliance(artifact,AllCompliance,&lowlight,exception);
198 (void) QueryColorCompliance(
"#888888cc",AllCompliance,&masklight,exception);
199 artifact=GetImageArtifact(image,
"compare:masklight-color");
200 if (artifact != (
const char *) NULL)
201 (void) QueryColorCompliance(artifact,AllCompliance,&masklight,exception);
205 image_view=AcquireVirtualCacheView(image,exception);
206 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
207 highlight_view=AcquireAuthenticCacheView(highlight_image,exception);
208#if defined(MAGICKCORE_OPENMP_SUPPORT)
209 #pragma omp parallel for schedule(static) shared(status) \
210 magick_number_threads(image,highlight_image,rows,1)
212 for (y=0; y < (ssize_t) rows; y++)
227 if (status == MagickFalse)
229 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
230 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
231 r=QueueCacheViewAuthenticPixels(highlight_view,0,y,columns,1,exception);
232 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL) ||
233 (r == (Quantum *) NULL))
238 for (x=0; x < (ssize_t) columns; x++)
240 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
241 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
243 SetPixelViaPixelInfo(highlight_image,&masklight,r);
244 p+=(ptrdiff_t) GetPixelChannels(image);
245 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
246 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
249 if (IsFuzzyEquivalencePixel(image,p,reconstruct_image,q) == MagickFalse)
250 SetPixelViaPixelInfo(highlight_image,&highlight,r);
252 SetPixelViaPixelInfo(highlight_image,&lowlight,r);
253 p+=(ptrdiff_t) GetPixelChannels(image);
254 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
255 r+=(ptrdiff_t) GetPixelChannels(highlight_image);
257 sync=SyncCacheViewAuthenticPixels(highlight_view,exception);
258 if (sync == MagickFalse)
261 highlight_view=DestroyCacheView(highlight_view);
262 reconstruct_view=DestroyCacheView(reconstruct_view);
263 image_view=DestroyCacheView(image_view);
264 if ((status != MagickFalse) && (difference_image != (Image *) NULL))
265 status=CompositeImage(difference_image,highlight_image,image->compose,
266 MagickTrue,0,0,exception);
267 highlight_image=DestroyImage(highlight_image);
268 if (status == MagickFalse)
269 difference_image=DestroyImage(difference_image);
270 return(difference_image);
307static MagickBooleanType GetAESimilarity(
const Image *image,
308 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
332 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
333 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
334 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
335 image_view=AcquireVirtualCacheView(image,exception);
336 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
337#if defined(MAGICKCORE_OPENMP_SUPPORT)
338 #pragma omp parallel for schedule(static) shared(similarity,status) \
339 magick_number_threads(image,image,rows,1)
341 for (y=0; y < (ssize_t) rows; y++)
348 channel_similarity[MaxPixelChannels+1] = { 0.0 };
353 if (status == MagickFalse)
355 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
356 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
357 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
362 for (x=0; x < (ssize_t) columns; x++)
371 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
372 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
374 p+=(ptrdiff_t) GetPixelChannels(image);
375 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
378 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
379 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
380 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
385 PixelChannel channel = GetPixelChannelChannel(image,i);
386 PixelTrait traits = GetPixelChannelTraits(image,channel);
387 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
389 if (((traits & UpdatePixelTrait) == 0) ||
390 ((reconstruct_traits & UpdatePixelTrait) == 0))
392 if (channel == AlphaPixelChannel)
393 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
396 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
397 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
399 double ae = fabs(QuantumScale*error);
400 channel_similarity[i]+=ae;
401 channel_similarity[CompositePixelChannel]+=ae;
404 p+=(ptrdiff_t) GetPixelChannels(image);
405 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
407#if defined(MAGICKCORE_OPENMP_SUPPORT)
408 #pragma omp critical (MagickCore_GetAESimilarity)
414 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
416 PixelChannel channel = GetPixelChannelChannel(image,j);
417 PixelTrait traits = GetPixelChannelTraits(image,channel);
418 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
420 if (((traits & UpdatePixelTrait) == 0) ||
421 ((reconstruct_traits & UpdatePixelTrait) == 0))
423 similarity[j]+=channel_similarity[j];
425 similarity[CompositePixelChannel]+=
426 channel_similarity[CompositePixelChannel];
429 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
430 reconstruct_view=DestroyCacheView(reconstruct_view);
431 image_view=DestroyCacheView(image_view);
432 area=MagickSafeReciprocal((
double) columns*rows);
433 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
435 similarity[CompositePixelChannel]*=area;
439static MagickBooleanType GetDPCSimilarity(
const Image *image,
440 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
442#define SimilarityImageTag "Similarity/Image"
450 *reconstruct_statistics;
453 norm[MaxPixelChannels+1] = { 0.0 },
454 reconstruct_norm[MaxPixelChannels+1] = { 0.0 };
473 image_statistics=GetImageStatistics(image,exception);
474 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
475 if ((image_statistics == (ChannelStatistics *) NULL) ||
476 (reconstruct_statistics == (ChannelStatistics *) NULL))
478 if (image_statistics != (ChannelStatistics *) NULL)
479 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
481 if (reconstruct_statistics != (ChannelStatistics *) NULL)
482 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
483 reconstruct_statistics);
486 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
487 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
488 image_view=AcquireVirtualCacheView(image,exception);
489 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
490#if defined(MAGICKCORE_OPENMP_SUPPORT)
491 #pragma omp parallel for schedule(static) shared(norm,reconstruct_norm,similarity,status) \
492 magick_number_threads(image,image,rows,1)
494 for (y=0; y < (ssize_t) rows; y++)
501 channel_norm[MaxPixelChannels+1] = { 0.0 },
502 channel_reconstruct_norm[MaxPixelChannels+1] = { 0.0 },
503 channel_similarity[MaxPixelChannels+1] = { 0.0 };
508 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
509 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
510 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
515 for (x=0; x < (ssize_t) columns; x++)
524 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
525 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
527 p+=(ptrdiff_t) GetPixelChannels(image);
528 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
531 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
532 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
533 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
539 PixelChannel channel = GetPixelChannelChannel(image,i);
540 PixelTrait traits = GetPixelChannelTraits(image,channel);
541 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
543 if (((traits & UpdatePixelTrait) == 0) ||
544 ((reconstruct_traits & UpdatePixelTrait) == 0))
546 if (channel == AlphaPixelChannel)
548 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
549 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
550 channel,q)-reconstruct_statistics[channel].mean);
554 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
555 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
556 q)-reconstruct_statistics[channel].mean);
558 channel_similarity[i]+=alpha*beta;
559 channel_norm[i]+=alpha*alpha;
560 channel_reconstruct_norm[i]+=beta*beta;
562 p+=(ptrdiff_t) GetPixelChannels(image);
563 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
565#if defined(MAGICKCORE_OPENMP_SUPPORT)
566 #pragma omp critical (MagickCore_GetDPCSimilarity)
572 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
574 PixelChannel channel = GetPixelChannelChannel(image,j);
575 PixelTrait traits = GetPixelChannelTraits(image,channel);
576 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
578 if (((traits & UpdatePixelTrait) == 0) ||
579 ((reconstruct_traits & UpdatePixelTrait) == 0))
581 similarity[j]+=channel_similarity[j];
582 similarity[CompositePixelChannel]+=channel_similarity[j];
583 norm[j]+=channel_norm[j];
584 norm[CompositePixelChannel]+=channel_norm[j];
585 reconstruct_norm[j]+=channel_reconstruct_norm[j];
586 reconstruct_norm[CompositePixelChannel]+=channel_reconstruct_norm[j];
589 if (image->progress_monitor != (MagickProgressMonitor) NULL)
594#if defined(MAGICKCORE_OPENMP_SUPPORT)
598 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
599 if (proceed == MagickFalse)
606 reconstruct_view=DestroyCacheView(reconstruct_view);
607 image_view=DestroyCacheView(image_view);
611 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
613 PixelChannel channel = GetPixelChannelChannel(image,k);
614 PixelTrait traits = GetPixelChannelTraits(image,channel);
615 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
617 if (((traits & UpdatePixelTrait) == 0) ||
618 ((reconstruct_traits & UpdatePixelTrait) == 0))
620 similarity[k]*=MagickSafeReciprocal(sqrt(norm[k]*reconstruct_norm[k]));
622 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
623 norm[CompositePixelChannel]*reconstruct_norm[CompositePixelChannel]));
627 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
628 reconstruct_statistics);
629 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
634static MagickBooleanType GetFUZZSimilarity(
const Image *image,
635 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
659 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
660 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
661 image_view=AcquireVirtualCacheView(image,exception);
662 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
663#if defined(MAGICKCORE_OPENMP_SUPPORT)
664 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
665 magick_number_threads(image,image,rows,1)
667 for (y=0; y < (ssize_t) rows; y++)
675 channel_similarity[MaxPixelChannels+1] = { 0.0 };
680 if (status == MagickFalse)
682 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
683 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
684 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
689 for (x=0; x < (ssize_t) columns; x++)
698 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
699 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
701 p+=(ptrdiff_t) GetPixelChannels(image);
702 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
705 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
706 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
707 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
712 PixelChannel channel = GetPixelChannelChannel(image,i);
713 PixelTrait traits = GetPixelChannelTraits(image,channel);
714 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
716 if (((traits & UpdatePixelTrait) == 0) ||
717 ((reconstruct_traits & UpdatePixelTrait) == 0))
719 if (channel == AlphaPixelChannel)
720 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
723 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
724 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
726 channel_similarity[i]+=QuantumScale*error*QuantumScale*error;
727 channel_similarity[CompositePixelChannel]+=QuantumScale*error*
732 p+=(ptrdiff_t) GetPixelChannels(image);
733 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
735#if defined(MAGICKCORE_OPENMP_SUPPORT)
736 #pragma omp critical (MagickCore_GetFUZZSimilarity)
743 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
745 PixelChannel channel = GetPixelChannelChannel(image,j);
746 PixelTrait traits = GetPixelChannelTraits(image,channel);
747 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
749 if (((traits & UpdatePixelTrait) == 0) ||
750 ((reconstruct_traits & UpdatePixelTrait) == 0))
752 similarity[j]+=channel_similarity[j];
754 similarity[CompositePixelChannel]+=
755 channel_similarity[CompositePixelChannel];
758 reconstruct_view=DestroyCacheView(reconstruct_view);
759 image_view=DestroyCacheView(image_view);
760 area=MagickSafeReciprocal(area);
761 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
763 PixelChannel channel = GetPixelChannelChannel(image,k);
764 PixelTrait traits = GetPixelChannelTraits(image,channel);
765 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
767 if (((traits & UpdatePixelTrait) == 0) ||
768 ((reconstruct_traits & UpdatePixelTrait) == 0))
772 similarity[CompositePixelChannel]*=area;
776static MagickBooleanType GetMAESimilarity(
const Image *image,
777 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
800 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
801 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
802 image_view=AcquireVirtualCacheView(image,exception);
803 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
804#if defined(MAGICKCORE_OPENMP_SUPPORT)
805 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
806 magick_number_threads(image,image,rows,1)
808 for (y=0; y < (ssize_t) rows; y++)
816 channel_similarity[MaxPixelChannels+1] = { 0.0 };
821 if (status == MagickFalse)
823 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
824 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
825 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
830 for (x=0; x < (ssize_t) columns; x++)
839 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
840 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
842 p+=(ptrdiff_t) GetPixelChannels(image);
843 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
846 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
847 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
848 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
853 PixelChannel channel = GetPixelChannelChannel(image,i);
854 PixelTrait traits = GetPixelChannelTraits(image,channel);
855 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
857 if (((traits & UpdatePixelTrait) == 0) ||
858 ((reconstruct_traits & UpdatePixelTrait) == 0))
860 if (channel == AlphaPixelChannel)
861 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
862 reconstruct_image,channel,q));
864 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
866 channel_similarity[i]+=error;
867 channel_similarity[CompositePixelChannel]+=error;
870 p+=(ptrdiff_t) GetPixelChannels(image);
871 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
873#if defined(MAGICKCORE_OPENMP_SUPPORT)
874 #pragma omp critical (MagickCore_GetMAESimilarity)
881 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
883 PixelChannel channel = GetPixelChannelChannel(image,j);
884 PixelTrait traits = GetPixelChannelTraits(image,channel);
885 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
887 if (((traits & UpdatePixelTrait) == 0) ||
888 ((reconstruct_traits & UpdatePixelTrait) == 0))
890 similarity[j]+=channel_similarity[j];
892 similarity[CompositePixelChannel]+=
893 channel_similarity[CompositePixelChannel];
896 reconstruct_view=DestroyCacheView(reconstruct_view);
897 image_view=DestroyCacheView(image_view);
898 area=MagickSafeReciprocal(area);
899 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
901 PixelChannel channel = GetPixelChannelChannel(image,k);
902 PixelTrait traits = GetPixelChannelTraits(image,channel);
903 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
905 if (((traits & UpdatePixelTrait) == 0) ||
906 ((reconstruct_traits & UpdatePixelTrait) == 0))
910 similarity[CompositePixelChannel]*=area;
911 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
915static MagickBooleanType GetMEPPSimilarity(Image *image,
916 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
924 maximum_error = -MagickMaximumValue,
941 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
942 image_view=AcquireVirtualCacheView(image,exception);
943 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
944#if defined(MAGICKCORE_OPENMP_SUPPORT)
945 #pragma omp parallel for schedule(static) shared(area,similarity,maximum_error,mean_error,status) \
946 magick_number_threads(image,image,rows,1)
948 for (y=0; y < (ssize_t) rows; y++)
956 channel_similarity[MaxPixelChannels+1] = { 0.0 },
957 channel_maximum_error = maximum_error,
958 channel_mean_error = 0.0;
963 if (status == MagickFalse)
965 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
966 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
967 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
972 for (x=0; x < (ssize_t) columns; x++)
981 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
982 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
984 p+=(ptrdiff_t) GetPixelChannels(image);
985 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
988 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
989 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
990 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
995 PixelChannel channel = GetPixelChannelChannel(image,i);
996 PixelTrait traits = GetPixelChannelTraits(image,channel);
997 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
999 if (((traits & UpdatePixelTrait) == 0) ||
1000 ((reconstruct_traits & UpdatePixelTrait) == 0))
1002 if (channel == AlphaPixelChannel)
1003 error=QuantumScale*fabs((
double) p[i]-(
double) GetPixelChannel(
1004 reconstruct_image,channel,q));
1006 error=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1008 channel_similarity[i]+=error;
1009 channel_similarity[CompositePixelChannel]+=error;
1010 channel_mean_error+=error*error;
1011 if (error > channel_maximum_error)
1012 channel_maximum_error=error;
1015 p+=(ptrdiff_t) GetPixelChannels(image);
1016 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1018#if defined(MAGICKCORE_OPENMP_SUPPORT)
1019 #pragma omp critical (MagickCore_GetMEPPSimilarity)
1026 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1028 PixelChannel channel = GetPixelChannelChannel(image,j);
1029 PixelTrait traits = GetPixelChannelTraits(image,channel);
1030 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1032 if (((traits & UpdatePixelTrait) == 0) ||
1033 ((reconstruct_traits & UpdatePixelTrait) == 0))
1035 similarity[j]+=channel_similarity[j];
1037 similarity[CompositePixelChannel]+=
1038 channel_similarity[CompositePixelChannel];
1039 mean_error+=channel_mean_error;
1040 if (channel_maximum_error > maximum_error)
1041 maximum_error=channel_maximum_error;
1044 reconstruct_view=DestroyCacheView(reconstruct_view);
1045 image_view=DestroyCacheView(image_view);
1046 area=MagickSafeReciprocal(area);
1047 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1049 PixelChannel channel = GetPixelChannelChannel(image,k);
1050 PixelTrait traits = GetPixelChannelTraits(image,channel);
1051 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1053 if (((traits & UpdatePixelTrait) == 0) ||
1054 ((reconstruct_traits & UpdatePixelTrait) == 0))
1056 similarity[k]*=area;
1058 similarity[CompositePixelChannel]*=area;
1059 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1060 image->error.mean_error_per_pixel=QuantumRange*
1061 similarity[CompositePixelChannel];
1062 image->error.normalized_mean_error=mean_error*area;
1063 image->error.normalized_maximum_error=maximum_error;
1067static MagickBooleanType GetMSESimilarity(
const Image *image,
1068 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1078 status = MagickTrue;
1091 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1092 image_view=AcquireVirtualCacheView(image,exception);
1093 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1094#if defined(MAGICKCORE_OPENMP_SUPPORT)
1095 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1096 magick_number_threads(image,image,rows,1)
1098 for (y=0; y < (ssize_t) rows; y++)
1106 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1111 if (status == MagickFalse)
1113 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1114 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1115 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1120 for (x=0; x < (ssize_t) columns; x++)
1129 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1130 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1132 p+=(ptrdiff_t) GetPixelChannels(image);
1133 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1136 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1137 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1138 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1143 PixelChannel channel = GetPixelChannelChannel(image,i);
1144 PixelTrait traits = GetPixelChannelTraits(image,channel);
1145 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1147 if (((traits & UpdatePixelTrait) == 0) ||
1148 ((reconstruct_traits & UpdatePixelTrait) == 0))
1150 if (channel == AlphaPixelChannel)
1151 error=QuantumScale*((double) p[i]-(double) GetPixelChannel(
1152 reconstruct_image,channel,q));
1154 error=QuantumScale*(Sa*p[i]-Da*GetPixelChannel(reconstruct_image,
1156 channel_similarity[i]+=error*error;
1157 channel_similarity[CompositePixelChannel]+=error*error;
1160 p+=(ptrdiff_t) GetPixelChannels(image);
1161 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1163#if defined(MAGICKCORE_OPENMP_SUPPORT)
1164 #pragma omp critical (MagickCore_GetMSESimilarity)
1171 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1173 PixelChannel channel = GetPixelChannelChannel(image,j);
1174 PixelTrait traits = GetPixelChannelTraits(image,channel);
1175 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1177 if (((traits & UpdatePixelTrait) == 0) ||
1178 ((reconstruct_traits & UpdatePixelTrait) == 0))
1180 similarity[j]+=channel_similarity[j];
1182 similarity[CompositePixelChannel]+=
1183 channel_similarity[CompositePixelChannel];
1186 reconstruct_view=DestroyCacheView(reconstruct_view);
1187 image_view=DestroyCacheView(image_view);
1188 area=MagickSafeReciprocal(area);
1189 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1191 PixelChannel channel = GetPixelChannelChannel(image,k);
1192 PixelTrait traits = GetPixelChannelTraits(image,channel);
1193 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1195 if (((traits & UpdatePixelTrait) == 0) ||
1196 ((reconstruct_traits & UpdatePixelTrait) == 0))
1198 similarity[k]*=area;
1200 similarity[CompositePixelChannel]*=area;
1201 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
1205static MagickBooleanType GetNCCSimilarity(
const Image *image,
1206 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1214 *reconstruct_statistics;
1217 reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1218 variance[MaxPixelChannels+1] = { 0.0 };
1221 status = MagickTrue;
1237 image_statistics=GetImageStatistics(image,exception);
1238 reconstruct_statistics=GetImageStatistics(reconstruct_image,exception);
1239 if ((image_statistics == (ChannelStatistics *) NULL) ||
1240 (reconstruct_statistics == (ChannelStatistics *) NULL))
1242 if (image_statistics != (ChannelStatistics *) NULL)
1243 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1245 if (reconstruct_statistics != (ChannelStatistics *) NULL)
1246 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1247 reconstruct_statistics);
1248 return(MagickFalse);
1250 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1251 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1252 image_view=AcquireVirtualCacheView(image,exception);
1253 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1254#if defined(MAGICKCORE_OPENMP_SUPPORT)
1255 #pragma omp parallel for schedule(static) shared(variance,reconstruct_variance,similarity,status) \
1256 magick_number_threads(image,image,rows,1)
1258 for (y=0; y < (ssize_t) rows; y++)
1265 channel_reconstruct_variance[MaxPixelChannels+1] = { 0.0 },
1266 channel_similarity[MaxPixelChannels+1] = { 0.0 },
1267 channel_variance[MaxPixelChannels+1] = { 0.0 };
1272 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1273 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1274 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1279 for (x=0; x < (ssize_t) columns; x++)
1288 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1289 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1291 p+=(ptrdiff_t) GetPixelChannels(image);
1292 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1295 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1296 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1297 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1303 PixelChannel channel = GetPixelChannelChannel(image,i);
1304 PixelTrait traits = GetPixelChannelTraits(image,channel);
1305 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1307 if (((traits & UpdatePixelTrait) == 0) ||
1308 ((reconstruct_traits & UpdatePixelTrait) == 0))
1310 if (channel == AlphaPixelChannel)
1312 alpha=QuantumScale*((double) p[i]-image_statistics[channel].mean);
1313 beta=QuantumScale*((double) GetPixelChannel(reconstruct_image,
1314 channel,q)-reconstruct_statistics[channel].mean);
1318 alpha=QuantumScale*(Sa*p[i]-image_statistics[channel].mean);
1319 beta=QuantumScale*(Da*GetPixelChannel(reconstruct_image,channel,
1320 q)-reconstruct_statistics[channel].mean);
1322 channel_similarity[i]+=alpha*beta;
1323 channel_variance[i]+=alpha*alpha;
1324 channel_reconstruct_variance[i]+=beta*beta;
1326 p+=(ptrdiff_t) GetPixelChannels(image);
1327 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1329#if defined(MAGICKCORE_OPENMP_SUPPORT)
1330 #pragma omp critical (MagickCore_GetNCCSimilarity)
1336 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1338 PixelChannel channel = GetPixelChannelChannel(image,j);
1339 PixelTrait traits = GetPixelChannelTraits(image,channel);
1340 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1342 if (((traits & UpdatePixelTrait) == 0) ||
1343 ((reconstruct_traits & UpdatePixelTrait) == 0))
1345 similarity[j]+=channel_similarity[j];
1346 similarity[CompositePixelChannel]+=channel_similarity[j];
1347 variance[j]+=channel_variance[j];
1348 variance[CompositePixelChannel]+=channel_variance[j];
1349 reconstruct_variance[j]+=channel_reconstruct_variance[j];
1350 reconstruct_variance[CompositePixelChannel]+=
1351 channel_reconstruct_variance[j];
1354 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1359#if defined(MAGICKCORE_OPENMP_SUPPORT)
1363 proceed=SetImageProgress(image,SimilarityImageTag,progress,rows);
1364 if (proceed == MagickFalse)
1371 reconstruct_view=DestroyCacheView(reconstruct_view);
1372 image_view=DestroyCacheView(image_view);
1376 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1378 PixelChannel channel = GetPixelChannelChannel(image,k);
1379 PixelTrait traits = GetPixelChannelTraits(image,channel);
1380 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1382 if (((traits & UpdatePixelTrait) == 0) ||
1383 ((reconstruct_traits & UpdatePixelTrait) == 0))
1385 similarity[k]*=MagickSafeReciprocal(sqrt(variance[k])*
1386 sqrt(reconstruct_variance[k]));
1388 similarity[CompositePixelChannel]*=MagickSafeReciprocal(sqrt(
1389 variance[CompositePixelChannel])*sqrt(
1390 reconstruct_variance[CompositePixelChannel]));
1394 reconstruct_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1395 reconstruct_statistics);
1396 image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1401static MagickBooleanType GetPASimilarity(
const Image *image,
1402 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1409 status = MagickTrue;
1421 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1422 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1423 image_view=AcquireVirtualCacheView(image,exception);
1424 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1425#if defined(MAGICKCORE_OPENMP_SUPPORT)
1426 #pragma omp parallel for schedule(static) shared(similarity,status) \
1427 magick_number_threads(image,image,rows,1)
1429 for (y=0; y < (ssize_t) rows; y++)
1436 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1441 if (status == MagickFalse)
1443 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1444 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1445 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1450 for (x=0; x < (ssize_t) columns; x++)
1459 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1460 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1462 p+=(ptrdiff_t) GetPixelChannels(image);
1463 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1466 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1467 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1473 PixelChannel channel = GetPixelChannelChannel(image,i);
1474 PixelTrait traits = GetPixelChannelTraits(image,channel);
1475 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1477 if (((traits & UpdatePixelTrait) == 0) ||
1478 ((reconstruct_traits & UpdatePixelTrait) == 0))
1480 if (channel == AlphaPixelChannel)
1481 distance=QuantumScale*fabs((
double) p[i]-(
double)
1482 GetPixelChannel(reconstruct_image,channel,q));
1484 distance=QuantumScale*fabs(Sa*p[i]-Da*GetPixelChannel(
1485 reconstruct_image,channel,q));
1486 if (distance > channel_similarity[i])
1487 channel_similarity[i]=distance;
1488 if (distance > channel_similarity[CompositePixelChannel])
1489 channel_similarity[CompositePixelChannel]=distance;
1491 p+=(ptrdiff_t) GetPixelChannels(image);
1492 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1494#if defined(MAGICKCORE_OPENMP_SUPPORT)
1495 #pragma omp critical (MagickCore_GetPASimilarity)
1501 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1503 PixelChannel channel = GetPixelChannelChannel(image,j);
1504 PixelTrait traits = GetPixelChannelTraits(image,channel);
1505 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1507 if (((traits & UpdatePixelTrait) == 0) ||
1508 ((reconstruct_traits & UpdatePixelTrait) == 0))
1510 if (channel_similarity[j] > similarity[j])
1511 similarity[j]=channel_similarity[j];
1513 if (channel_similarity[CompositePixelChannel] > similarity[CompositePixelChannel])
1514 similarity[CompositePixelChannel]=
1515 channel_similarity[CompositePixelChannel];
1518 reconstruct_view=DestroyCacheView(reconstruct_view);
1519 image_view=DestroyCacheView(image_view);
1523static MagickBooleanType GetPDCSimilarity(
const Image *image,
1524 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1534 status = MagickTrue;
1546 fuzz=GetFuzzyColorDistance(image,reconstruct_image);
1547 (void) memset(similarity,0,(MaxPixelChannels+1)*
sizeof(*similarity));
1548 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1549 image_view=AcquireVirtualCacheView(image,exception);
1550 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1551#if defined(MAGICKCORE_OPENMP_SUPPORT)
1552 #pragma omp parallel for schedule(static) shared(similarity,status) \
1553 magick_number_threads(image,image,rows,1)
1555 for (y=0; y < (ssize_t) rows; y++)
1562 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1567 if (status == MagickFalse)
1569 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1570 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1571 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1576 for (x=0; x < (ssize_t) columns; x++)
1588 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1589 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1591 p+=(ptrdiff_t) GetPixelChannels(image);
1592 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1595 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1596 Da=QuantumScale*(double) GetPixelAlpha(reconstruct_image,q);
1597 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1602 PixelChannel channel = GetPixelChannelChannel(image,i);
1603 PixelTrait traits = GetPixelChannelTraits(image,channel);
1604 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1606 if (((traits & UpdatePixelTrait) == 0) ||
1607 ((reconstruct_traits & UpdatePixelTrait) == 0))
1609 if (channel == AlphaPixelChannel)
1610 error=(double) p[i]-(
double) GetPixelChannel(reconstruct_image,
1613 error=Sa*p[i]-Da*GetPixelChannel(reconstruct_image,channel,q);
1614 if (MagickSafeSignificantError(error*error,fuzz) != MagickFalse)
1616 channel_similarity[i]++;
1621 channel_similarity[CompositePixelChannel]++;
1622 p+=(ptrdiff_t) GetPixelChannels(image);
1623 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1625#if defined(MAGICKCORE_OPENMP_SUPPORT)
1626 #pragma omp critical (MagickCore_GetPDCSimilarity)
1632 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1634 PixelChannel channel = GetPixelChannelChannel(image,j);
1635 PixelTrait traits = GetPixelChannelTraits(image,channel);
1636 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1638 if (((traits & UpdatePixelTrait) == 0) ||
1639 ((reconstruct_traits & UpdatePixelTrait) == 0))
1641 similarity[j]+=channel_similarity[j];
1643 similarity[CompositePixelChannel]+=
1644 channel_similarity[CompositePixelChannel];
1647 reconstruct_view=DestroyCacheView(reconstruct_view);
1648 image_view=DestroyCacheView(image_view);
1652static MemoryInfo *ComputeAllPhaseSpectra(
const Image *image,
const size_t rows,
1653 const size_t columns,ExceptionInfo *exception)
1655#define HeapOverflowCheck(a,b,c) \
1656 (((size_t) (a) != 0) && ((size_t) (b) <= SIZE_MAX / (size_t) (a)) && \
1657 ((size_t) (c) <= SIZE_MAX / ((size_t) (a) * (size_t)(b))))
1684 if (HeapOverflowCheck(GetPixelChannels(image),image->rows,2) == MagickFalse)
1685 return((MemoryInfo *) NULL);
1686 n_G=(size_t) 2*GetPixelChannels(image)*image->rows;
1687 if (HeapOverflowCheck(rows,columns,GetPixelChannels(image)) == MagickFalse)
1688 return((MemoryInfo *) NULL);
1689 n_phase=(size_t) rows*columns*GetPixelChannels(image);
1690 G_info=AcquireVirtualMemory(n_G,
sizeof(*G));
1691 phase_info=AcquireVirtualMemory(n_phase,
sizeof(*phase));
1692 if ((G_info == (MemoryInfo *) NULL) || (phase_info == (MemoryInfo *) NULL))
1694 if (G_info != (MemoryInfo *) NULL)
1695 G_info=RelinquishVirtualMemory(G_info);
1696 if (phase_info != (MemoryInfo *) NULL)
1697 phase_info=RelinquishVirtualMemory(phase_info);
1698 return((MemoryInfo *) NULL);
1700 G=(
double *) GetVirtualMemoryBlob(G_info);
1701 phase=(
double *) GetVirtualMemoryBlob(phase_info);
1702 (void) memset(phase,0,n_phase*
sizeof(*phase));
1704 image_view=AcquireVirtualCacheView(image,exception);
1705 for (u=0; u < (ssize_t) columns; u++)
1720 (void) memset(G,0,n_G*
sizeof(*G));
1721 theta_u=2.0*MagickPI*(double) u/(
double) image->rows;
1722 base_cos=cos(theta_u);
1723 base_sin=sin(theta_u);
1724 for (y=0; y < (ssize_t) image->rows; y++)
1736 if (status == MagickFalse)
1738 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1739 if (p == (
const Quantum *) NULL)
1746 for (x=0; x < (ssize_t) image->columns; x++)
1753 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1754 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1762 PixelChannel channel = GetPixelChannelChannel(image,k);
1763 PixelTrait traits = GetPixelChannelTraits(image,channel);
1764 if (traits == UndefinedPixelTrait)
1766 pixel=(channel == AlphaPixelChannel) ? QuantumScale*(
double) p[k] :
1767 QuantumScale*Sa*(double) p[k];
1768 i=((size_t) y*GetPixelChannels(image)+(size_t) k)*2;
1775 new_cx=cx*base_cos-sx*base_sin;
1776 new_sx=sx*base_cos+cx*base_sin;
1779 p+=(ptrdiff_t) GetPixelChannels(image);
1782 if (status == MagickFalse)
1784 for (v=0; v < (ssize_t) rows; v++)
1789 channel_imag[MaxPixelChannels+1],
1790 channel_real[MaxPixelChannels+1],
1798 (void) memset(channel_real,0,
sizeof(channel_real));
1799 (void) memset(channel_imag,0,
sizeof(channel_imag));
1800 theta_v=2.0*MagickPI*(double) v/(
double) image->columns;
1801 base_cos_v=cos(theta_v);
1802 base_sin_v=sin(theta_v);
1805 for (y=0; y < (ssize_t) image->rows; y++)
1811 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1816 PixelChannel channel = GetPixelChannelChannel(image,k);
1817 PixelTrait traits = GetPixelChannelTraits(image,channel);
1818 if (traits == UndefinedPixelTrait)
1820 i=2*((size_t) y*GetPixelChannels(image)+(size_t) k);
1821 channel_real[k]+=G[i]*cy-G[i+1]*sy;
1822 channel_imag[k]+=G[i+1]*cy+G[i]*sy;
1827 new_cy=cy*base_cos_v-sy*base_sin_v;
1828 new_sy=sy*base_cos_v+cy*base_sin_v;
1832 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1837 PixelChannel channel = GetPixelChannelChannel(image,k);
1838 PixelTrait traits = GetPixelChannelTraits(image,channel);
1839 if (traits == UndefinedPixelTrait)
1841 phase_idx=((size_t) v*columns+(size_t) u)*GetPixelChannels(image)+
1843 phase[phase_idx]=atan2(channel_imag[k],channel_real[k]);
1847 image_view=DestroyCacheView(image_view);
1848 G_info=(MemoryInfo *) RelinquishMagickMemory(G_info);
1849 if (status == MagickFalse)
1851 phase_info=RelinquishVirtualMemory(phase_info);
1852 return((MemoryInfo *) NULL);
1857static MagickBooleanType GetPHASESimilarity(
const Image *image,
1858 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1867 *reconstruct_spectra;
1870 status = MagickTrue;
1887 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1888 phase_info=ComputeAllPhaseSpectra(image,rows,columns,exception);
1889 reconstruct_info=ComputeAllPhaseSpectra(reconstruct_image,rows,columns,
1891 if ((phase_info == (MemoryInfo *) NULL) ||
1892 (reconstruct_info == (MemoryInfo *) NULL))
1894 if (phase_info != (MemoryInfo *) NULL)
1895 phase_info=RelinquishVirtualMemory(phase_info);
1896 if (reconstruct_info != (MemoryInfo *) NULL)
1897 reconstruct_info=RelinquishVirtualMemory(reconstruct_info);
1898 return(MagickFalse);
1900 phase_spectra=(
double *) GetVirtualMemoryBlob(phase_info);
1901 reconstruct_spectra=(
double *) GetVirtualMemoryBlob(reconstruct_info);
1902 image_view=AcquireVirtualCacheView(image,exception);
1903 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1904#if defined(MAGICKCORE_OPENMP_SUPPORT)
1905 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1906 magick_number_threads(image,image,rows,0.25)
1908 for (y=0; y < (ssize_t) rows; y++)
1916 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1921 if (status == MagickFalse)
1923 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1924 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1925 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1930 for (x=0; x < (ssize_t) columns; x++)
1935 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1936 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1938 p+=(ptrdiff_t) GetPixelChannels(image);
1939 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1942 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1947 PixelChannel channel = GetPixelChannelChannel(image,i);
1948 PixelTrait traits = GetPixelChannelTraits(image,channel);
1949 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1951 if (((traits & UpdatePixelTrait) == 0) ||
1952 ((reconstruct_traits & UpdatePixelTrait) == 0))
1954 delta=(phase_spectra[((size_t) y*columns+(size_t) x)*
1955 GetPixelChannels(image)+(size_t) i])-((i < (ssize_t)
1956 GetPixelChannels(reconstruct_image)) ?
1957 reconstruct_spectra[((size_t) y*columns+(size_t) x)*
1958 GetPixelChannels(reconstruct_image)+(size_t) i] : 0.0);
1959 channel_similarity[i]+=cos(delta);
1960 channel_similarity[CompositePixelChannel]+=cos(delta);
1963 p+=(ptrdiff_t) GetPixelChannels(image);
1964 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1966#if defined(MAGICKCORE_OPENMP_SUPPORT)
1967 #pragma omp critical (MagickCore_GetPHASESimilarity)
1974 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1976 PixelChannel channel = GetPixelChannelChannel(image,j);
1977 PixelTrait traits = GetPixelChannelTraits(image,channel);
1978 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1980 if (((traits & UpdatePixelTrait) == 0) ||
1981 ((reconstruct_traits & UpdatePixelTrait) == 0))
1983 similarity[j]+=channel_similarity[j];
1985 similarity[CompositePixelChannel]+=
1986 channel_similarity[CompositePixelChannel];
1989 reconstruct_view=DestroyCacheView(reconstruct_view);
1990 image_view=DestroyCacheView(image_view);
1991 phase_info=RelinquishVirtualMemory(phase_info);
1992 reconstruct_info=RelinquishVirtualMemory(reconstruct_info);
1993 area=MagickSafeReciprocal(area);
1994 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1996 PixelChannel channel = GetPixelChannelChannel(image,k);
1997 PixelTrait traits = GetPixelChannelTraits(image,channel);
1998 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2000 if (((traits & UpdatePixelTrait) == 0) ||
2001 ((reconstruct_traits & UpdatePixelTrait) == 0))
2003 similarity[k]*=area;
2005 similarity[CompositePixelChannel]*=area;
2006 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2010static MagickBooleanType GetPHASHSimilarity(
const Image *image,
2011 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2013 ChannelPerceptualHash
2026 channel_phash=GetImagePerceptualHash(image,exception);
2027 if (channel_phash == (ChannelPerceptualHash *) NULL)
2028 return(MagickFalse);
2029 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
2030 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
2032 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
2034 return(MagickFalse);
2036 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2044 PixelChannel channel = GetPixelChannelChannel(image,i);
2045 PixelTrait traits = GetPixelChannelTraits(image,channel);
2046 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2048 if (((traits & UpdatePixelTrait) == 0) ||
2049 ((reconstruct_traits & UpdatePixelTrait) == 0))
2051 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
2060 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
2065 alpha=channel_phash[i].phash[j][k];
2066 beta=reconstruct_phash[i].phash[j][k];
2068 if (IsNaN(error) != 0)
2070 difference+=error*error;
2073 similarity[i]+=difference;
2074 similarity[CompositePixelChannel]+=difference;
2076 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2077 artifact=GetImageArtifact(image,
"phash:normalize");
2078 if (IsStringTrue(artifact) != MagickFalse)
2080 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2082 PixelChannel channel = GetPixelChannelChannel(image,i);
2083 PixelTrait traits = GetPixelChannelTraits(image,channel);
2084 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2086 if (((traits & UpdatePixelTrait) == 0) ||
2087 ((reconstruct_traits & UpdatePixelTrait) == 0))
2089 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
2091 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
2092 channel_phash[0].number_colorspaces);
2097 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
2099 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
2103static MagickBooleanType GetPSNRSimilarity(
const Image *image,
2104 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2107 status = MagickTrue;
2115 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2116 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2118 PixelChannel channel = GetPixelChannelChannel(image,i);
2119 PixelTrait traits = GetPixelChannelTraits(image,channel);
2120 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2122 if (((traits & UpdatePixelTrait) == 0) ||
2123 ((reconstruct_traits & UpdatePixelTrait) == 0))
2125 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
2126 similarity[i]))/MagickSafePSNRRecipicol(10.0);
2128 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
2129 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
2130 MagickSafePSNRRecipicol(10.0);
2134static MagickBooleanType GetRMSESimilarity(
const Image *image,
2135 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2137#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2140 status = MagickTrue;
2148 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2149 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2151 PixelChannel channel = GetPixelChannelChannel(image,i);
2152 PixelTrait traits = GetPixelChannelTraits(image,channel);
2153 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2155 if (((traits & UpdatePixelTrait) == 0) ||
2156 ((reconstruct_traits & UpdatePixelTrait) == 0))
2158 similarity[i]=RMSESquareRoot(similarity[i]);
2160 similarity[CompositePixelChannel]=RMSESquareRoot(
2161 similarity[CompositePixelChannel]);
2165static MagickBooleanType GetSSIMSimularity(
const Image *image,
2166 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2168#define SSIMRadius 5.0
2169#define SSIMSigma 1.5
2179 geometry[MagickPathExtent];
2195 status = MagickTrue;
2209 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2210 if (artifact != (
const char *) NULL)
2211 radius=StringToDouble(artifact,(
char **) NULL);
2213 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2214 if (artifact != (
const char *) NULL)
2215 sigma=StringToDouble(artifact,(
char **) NULL);
2216 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.17gx%.17g",
2218 kernel_info=AcquireKernelInfo(geometry,exception);
2219 if (kernel_info == (KernelInfo *) NULL)
2220 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2222 c1=pow(SSIMK1*SSIML,2.0);
2223 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2224 if (artifact != (
const char *) NULL)
2225 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2226 c2=pow(SSIMK2*SSIML,2.0);
2227 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2228 if (artifact != (
const char *) NULL)
2229 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2230 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2231 image_view=AcquireVirtualCacheView(image,exception);
2232 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2233#if defined(MAGICKCORE_OPENMP_SUPPORT)
2234 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2235 magick_number_threads(image,reconstruct_image,rows,1)
2237 for (y=0; y < (ssize_t) rows; y++)
2245 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2251 if (status == MagickFalse)
2253 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2254 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2255 kernel_info->height,exception);
2256 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2257 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2258 kernel_info->height,exception);
2259 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2264 for (x=0; x < (ssize_t) columns; x++)
2267 *magick_restrict reconstruct,
2268 *magick_restrict test;
2271 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2272 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2273 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2274 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2275 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2283 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2284 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2286 p+=(ptrdiff_t) GetPixelChannels(image);
2287 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2290 k=kernel_info->values;
2293 for (v=0; v < (ssize_t) kernel_info->height; v++)
2298 for (u=0; u < (ssize_t) kernel_info->width; u++)
2300 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2306 PixelChannel channel = GetPixelChannelChannel(image,i);
2307 PixelTrait traits = GetPixelChannelTraits(image,channel);
2308 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2309 reconstruct_image,channel);
2310 if (((traits & UpdatePixelTrait) == 0) ||
2311 ((reconstruct_traits & UpdatePixelTrait) == 0))
2313 x_pixel=QuantumScale*(double) test[i];
2314 x_pixel_mu[i]+=(*k)*x_pixel;
2315 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2316 y_pixel=QuantumScale*(double)
2317 GetPixelChannel(reconstruct_image,channel,reconstruct);
2318 y_pixel_mu[i]+=(*k)*y_pixel;
2319 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2320 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2323 test+=(ptrdiff_t) GetPixelChannels(image);
2324 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2326 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2327 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2329 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2334 x_pixel_sigmas_squared,
2338 y_pixel_sigmas_squared;
2340 PixelChannel channel = GetPixelChannelChannel(image,i);
2341 PixelTrait traits = GetPixelChannelTraits(image,channel);
2342 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2343 reconstruct_image,channel);
2344 if (((traits & UpdatePixelTrait) == 0) ||
2345 ((reconstruct_traits & UpdatePixelTrait) == 0))
2347 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2348 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2349 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2350 xy_sigmas=xy_sigma[i]-xy_mu;
2351 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2352 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2353 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2354 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2355 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2356 channel_similarity[i]+=ssim;
2357 channel_similarity[CompositePixelChannel]+=ssim;
2359 p+=(ptrdiff_t) GetPixelChannels(image);
2360 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2363#if defined(MAGICKCORE_OPENMP_SUPPORT)
2364 #pragma omp critical (MagickCore_GetSSIMSimularity)
2371 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2373 PixelChannel channel = GetPixelChannelChannel(image,j);
2374 PixelTrait traits = GetPixelChannelTraits(image,channel);
2375 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2377 if (((traits & UpdatePixelTrait) == 0) ||
2378 ((reconstruct_traits & UpdatePixelTrait) == 0))
2380 similarity[j]+=channel_similarity[j];
2382 similarity[CompositePixelChannel]+=
2383 channel_similarity[CompositePixelChannel];
2386 image_view=DestroyCacheView(image_view);
2387 reconstruct_view=DestroyCacheView(reconstruct_view);
2388 area=MagickSafeReciprocal(area);
2389 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2391 PixelChannel channel = GetPixelChannelChannel(image,l);
2392 PixelTrait traits = GetPixelChannelTraits(image,channel);
2393 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2395 if (((traits & UpdatePixelTrait) == 0) ||
2396 ((reconstruct_traits & UpdatePixelTrait) == 0))
2398 similarity[l]*=area;
2400 similarity[CompositePixelChannel]*=area;
2401 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2402 kernel_info=DestroyKernelInfo(kernel_info);
2406static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2407 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2410 status = MagickTrue;
2418 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2419 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2421 PixelChannel channel = GetPixelChannelChannel(image,i);
2422 PixelTrait traits = GetPixelChannelTraits(image,channel);
2423 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2425 if (((traits & UpdatePixelTrait) == 0) ||
2426 ((reconstruct_traits & UpdatePixelTrait) == 0))
2428 similarity[i]=(1.0-similarity[i])/2.0;
2430 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2434MagickExport MagickBooleanType GetImageDistortion(Image *image,
2435 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2436 ExceptionInfo *exception)
2438#define CompareMetricNotSupportedException "metric not supported"
2441 *channel_similarity;
2444 status = MagickTrue;
2449 assert(image != (Image *) NULL);
2450 assert(image->signature == MagickCoreSignature);
2451 assert(reconstruct_image != (
const Image *) NULL);
2452 assert(reconstruct_image->signature == MagickCoreSignature);
2453 assert(distortion != (
double *) NULL);
2454 if (IsEventLogging() != MagickFalse)
2455 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2460 length=MaxPixelChannels+1UL;
2461 channel_similarity=(
double *) AcquireQuantumMemory(length,
2462 sizeof(*channel_similarity));
2463 if (channel_similarity == (
double *) NULL)
2464 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2465 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2468 case AbsoluteErrorMetric:
2470 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2474 case DotProductCorrelationErrorMetric:
2476 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2480 case FuzzErrorMetric:
2482 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2486 case MeanAbsoluteErrorMetric:
2488 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2492 case MeanErrorPerPixelErrorMetric:
2494 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2498 case MeanSquaredErrorMetric:
2500 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2504 case NormalizedCrossCorrelationErrorMetric:
2506 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2510 case PeakAbsoluteErrorMetric:
2512 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2516 case PeakSignalToNoiseRatioErrorMetric:
2518 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2522 case PerceptualHashErrorMetric:
2524 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2528 case PhaseCorrelationErrorMetric:
2530 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2534 case PixelDifferenceCountErrorMetric:
2536 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2540 case RootMeanSquaredErrorMetric:
2541 case UndefinedErrorMetric:
2544 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2548 case StructuralDissimilarityErrorMetric:
2550 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2554 case StructuralSimilarityErrorMetric:
2556 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2561 *distortion=channel_similarity[CompositePixelChannel];
2564 case DotProductCorrelationErrorMetric:
2565 case NormalizedCrossCorrelationErrorMetric:
2566 case PhaseCorrelationErrorMetric:
2567 case StructuralSimilarityErrorMetric:
2569 *distortion=(1.0-(*distortion))/2.0;
2574 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2575 if (fabs(*distortion) < MagickEpsilon)
2577 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2613MagickExport
double *GetImageDistortions(Image *image,
2614 const Image *reconstruct_image,
const MetricType metric,
2615 ExceptionInfo *exception)
2619 *channel_similarity;
2622 status = MagickTrue;
2630 assert(image != (Image *) NULL);
2631 assert(image->signature == MagickCoreSignature);
2632 assert(reconstruct_image != (
const Image *) NULL);
2633 assert(reconstruct_image->signature == MagickCoreSignature);
2634 if (IsEventLogging() != MagickFalse)
2635 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2639 length=MaxPixelChannels+1UL;
2640 channel_similarity=(
double *) AcquireQuantumMemory(length,
2641 sizeof(*channel_similarity));
2642 if (channel_similarity == (
double *) NULL)
2643 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2644 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2647 case AbsoluteErrorMetric:
2649 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2653 case DotProductCorrelationErrorMetric:
2655 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2659 case FuzzErrorMetric:
2661 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2665 case MeanAbsoluteErrorMetric:
2667 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2671 case MeanErrorPerPixelErrorMetric:
2673 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2677 case MeanSquaredErrorMetric:
2679 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2683 case NormalizedCrossCorrelationErrorMetric:
2685 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2689 case PeakAbsoluteErrorMetric:
2691 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2695 case PeakSignalToNoiseRatioErrorMetric:
2697 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2701 case PerceptualHashErrorMetric:
2703 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2707 case PhaseCorrelationErrorMetric:
2709 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2713 case PixelDifferenceCountErrorMetric:
2715 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2719 case RootMeanSquaredErrorMetric:
2720 case UndefinedErrorMetric:
2723 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2727 case StructuralDissimilarityErrorMetric:
2729 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2733 case StructuralSimilarityErrorMetric:
2735 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2740 if (status == MagickFalse)
2742 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2743 return((
double *) NULL);
2745 distortion=channel_similarity;
2748 case DotProductCorrelationErrorMetric:
2749 case NormalizedCrossCorrelationErrorMetric:
2750 case PhaseCorrelationErrorMetric:
2751 case StructuralSimilarityErrorMetric:
2753 for (i=0; i <= MaxPixelChannels; i++)
2754 distortion[i]=(1.0-distortion[i])/2.0;
2759 for (i=0; i <= MaxPixelChannels; i++)
2760 if (fabs(distortion[i]) < MagickEpsilon)
2762 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2763 distortion[CompositePixelChannel]);
2795MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2796 const Image *reconstruct_image,ExceptionInfo *exception)
2809 assert(image != (Image *) NULL);
2810 assert(image->signature == MagickCoreSignature);
2811 assert(reconstruct_image != (
const Image *) NULL);
2812 assert(reconstruct_image->signature == MagickCoreSignature);
2813 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2814 image_view=AcquireVirtualCacheView(image,exception);
2815 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2816 for (y=0; y < (ssize_t) rows; y++)
2825 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2826 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2827 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2829 for (x=0; x < (ssize_t) columns; x++)
2834 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2839 PixelChannel channel = GetPixelChannelChannel(image,i);
2840 PixelTrait traits = GetPixelChannelTraits(image,channel);
2841 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2843 if (((traits & UpdatePixelTrait) == 0) ||
2844 ((reconstruct_traits & UpdatePixelTrait) == 0))
2846 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2848 if (distance >= MagickEpsilon)
2851 if (i < (ssize_t) GetPixelChannels(image))
2853 p+=(ptrdiff_t) GetPixelChannels(image);
2854 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2856 if (x < (ssize_t) columns)
2859 reconstruct_view=DestroyCacheView(reconstruct_view);
2860 image_view=DestroyCacheView(image_view);
2861 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2913MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2914 const Image *reconstruct_image,ExceptionInfo *exception)
2917 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2922 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2924 if (status == MagickFalse)
2925 return(MagickFalse);
2926 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2927 MagickTrue : MagickFalse;
2974#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2975static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2976 const Image *beta_image,ExceptionInfo *exception)
2979 *alpha_fft = (Image *) NULL,
2980 *beta_fft = (Image *) NULL,
2981 *complex_conjugate = (Image *) NULL,
2982 *complex_multiplication = (Image *) NULL,
2983 *cross_correlation = (Image *) NULL,
2984 *temp_image = (Image *) NULL;
2989 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2990 if (temp_image == (Image *) NULL)
2991 return((Image *) NULL);
2992 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2993 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2994 temp_image=DestroyImageList(temp_image);
2995 if (beta_fft == (Image *) NULL)
2996 return((Image *) NULL);
3000 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3001 beta_fft=DestroyImageList(beta_fft);
3002 if (complex_conjugate == (Image *) NULL)
3003 return((Image *) NULL);
3007 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3008 if (temp_image == (Image *) NULL)
3010 complex_conjugate=DestroyImageList(complex_conjugate);
3011 return((Image *) NULL);
3013 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
3014 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
3015 temp_image=DestroyImageList(temp_image);
3016 if (alpha_fft == (Image *) NULL)
3018 complex_conjugate=DestroyImageList(complex_conjugate);
3019 return((Image *) NULL);
3024 DisableCompositeClampUnlessSpecified(complex_conjugate);
3025 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
3026 AppendImageToList(&complex_conjugate,alpha_fft);
3027 complex_multiplication=ComplexImages(complex_conjugate,
3028 MultiplyComplexOperator,exception);
3029 complex_conjugate=DestroyImageList(complex_conjugate);
3030 if (complex_multiplication == (Image *) NULL)
3031 return((Image *) NULL);
3035 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3036 complex_multiplication->next,MagickFalse,exception);
3037 complex_multiplication=DestroyImageList(complex_multiplication);
3038 return(cross_correlation);
3041static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
3042 ExceptionInfo *exception)
3050 kernel_info=AcquireKernelInfo(kernel,exception);
3051 if (kernel_info == (KernelInfo *) NULL)
3052 return((Image *) NULL);
3053 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3055 kernel_info=DestroyKernelInfo(kernel_info);
3056 return(derivative_image);
3059static Image *SIMDivideImage(
const Image *numerator_image,
3060 const Image *denominator_image,ExceptionInfo *exception)
3070 status = MagickTrue;
3078 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
3079 if (divide_image == (Image *) NULL)
3080 return(divide_image);
3081 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
3082 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
3083#if defined(MAGICKCORE_OPENMP_SUPPORT)
3084 #pragma omp parallel for schedule(static) shared(status) \
3085 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
3087 for (y=0; y < (ssize_t) divide_image->rows; y++)
3098 if (status == MagickFalse)
3100 p=GetCacheViewVirtualPixels(denominator_view,0,y,
3101 denominator_image->columns,1,exception);
3102 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
3104 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3109 for (x=0; x < (ssize_t) divide_image->columns; x++)
3114 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
3116 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
3117 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
3118 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
3120 if (((traits & UpdatePixelTrait) == 0) ||
3121 ((denominator_traits & UpdatePixelTrait) == 0))
3123 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
3124 (
double) GetPixelChannel(denominator_image,channel,p)));
3126 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
3127 q+=(ptrdiff_t) GetPixelChannels(divide_image);
3129 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
3132 denominator_view=DestroyCacheView(denominator_view);
3133 numerator_view=DestroyCacheView(numerator_view);
3134 if (status == MagickFalse)
3135 divide_image=DestroyImage(divide_image);
3136 return(divide_image);
3139static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3140 const Image *source_image,ExceptionInfo *exception)
3149 divide_image=SIMDivideImage(image,magnitude_image,exception);
3150 if (divide_image == (Image *) NULL)
3151 return((Image *) NULL);
3152 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3153 ÷_image->background_color);
3154 SetGeometry(source_image,&geometry);
3155 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3156 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3157 result_image=ExtentImage(divide_image,&geometry,exception);
3158 divide_image=DestroyImage(divide_image);
3159 return(result_image);
3162static MagickBooleanType SIMFilterImageNaNs(Image *image,
3163 ExceptionInfo *exception)
3169 status = MagickTrue;
3177 image_view=AcquireAuthenticCacheView(image,exception);
3178#if defined(MAGICKCORE_OPENMP_SUPPORT)
3179 #pragma omp parallel for schedule(static) shared(status) \
3180 magick_number_threads(image,image,image->rows,1)
3182 for (y=0; y < (ssize_t) image->rows; y++)
3190 if (status == MagickFalse)
3192 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3193 if (q == (Quantum *) NULL)
3198 for (x=0; x < (ssize_t) image->columns; x++)
3203 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3205 PixelChannel channel = GetPixelChannelChannel(image,i);
3206 PixelTrait traits = GetPixelChannelTraits(image,channel);
3207 if ((traits & UpdatePixelTrait) == 0)
3209 if (IsNaN((
double) q[i]) != 0)
3212 q+=(ptrdiff_t) GetPixelChannels(image);
3214 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3217 image_view=DestroyCacheView(image_view);
3221static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3230 status = MagickTrue;
3238 square_image=CloneImage(image,0,0,MagickTrue,exception);
3239 if (square_image == (Image *) NULL)
3240 return(square_image);
3241 image_view=AcquireAuthenticCacheView(square_image,exception);
3242#if defined(MAGICKCORE_OPENMP_SUPPORT)
3243 #pragma omp parallel for schedule(static) shared(status) \
3244 magick_number_threads(square_image,square_image,square_image->rows,1)
3246 for (y=0; y < (ssize_t) square_image->rows; y++)
3254 if (status == MagickFalse)
3256 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3258 if (q == (Quantum *) NULL)
3263 for (x=0; x < (ssize_t) square_image->columns; x++)
3268 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3270 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3271 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3272 if ((traits & UpdatePixelTrait) == 0)
3274 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3276 q+=(ptrdiff_t) GetPixelChannels(square_image);
3278 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3281 image_view=DestroyCacheView(image_view);
3282 if (status == MagickFalse)
3283 square_image=DestroyImage(square_image);
3284 return(square_image);
3287static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3288 ExceptionInfo *exception)
3296 status = MagickTrue;
3298 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3299 xsq_image=SIMSquareImage(alpha_image,exception);
3300 if (xsq_image == (Image *) NULL)
3301 return((Image *) NULL);
3302 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3303 ysq_image=SIMSquareImage(beta_image,exception);
3304 if (ysq_image == (Image *) NULL)
3306 xsq_image=DestroyImage(xsq_image);
3307 return((Image *) NULL);
3309 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3311 magnitude_image=xsq_image;
3312 ysq_image=DestroyImage(ysq_image);
3313 if (status == MagickFalse)
3315 magnitude_image=DestroyImage(magnitude_image);
3316 return((Image *) NULL);
3318 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3319 if (status == MagickFalse)
3321 magnitude_image=DestroyImage(magnitude_image);
3322 return (Image *) NULL;
3324 return(magnitude_image);
3327static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3328 RectangleInfo *offset,ExceptionInfo *exception)
3347 status = MagickTrue;
3350 maxima_info = { -MagickMaximumValue, 0, 0 };
3358 image_view=AcquireVirtualCacheView(image,exception);
3359 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3361 if (q != (
const Quantum *) NULL)
3362 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3363#if defined(MAGICKCORE_OPENMP_SUPPORT)
3364 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3365 magick_number_threads(image,image,image->rows,1)
3367 for (y=0; y < (ssize_t) image->rows; y++)
3373 channel_maxima = { -MagickMaximumValue, 0, 0 };
3378 if (status == MagickFalse)
3380 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3381 if (p == (
const Quantum *) NULL)
3386 channel_maxima=maxima_info;
3387 for (x=0; x < (ssize_t) image->columns; x++)
3392 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3397 PixelChannel channel = GetPixelChannelChannel(image,i);
3398 PixelTrait traits = GetPixelChannelTraits(image,channel);
3399 if ((traits & UpdatePixelTrait) == 0)
3401 pixel=(double) p[i];
3402 if (IsNaN(pixel) != 0)
3404 if (pixel > channel_maxima.maxima)
3406 channel_maxima.maxima=(double) p[i];
3411 p+=(ptrdiff_t) GetPixelChannels(image);
3413#if defined(MAGICKCORE_OPENMP_SUPPORT)
3414 #pragma omp critical (MagickCore_SIMMaximaImage)
3416 if (channel_maxima.maxima > maxima_info.maxima)
3417 maxima_info=channel_maxima;
3419 image_view=DestroyCacheView(image_view);
3420 *maxima=maxima_info.maxima;
3421 offset->x=maxima_info.x;
3422 offset->y=maxima_info.y;
3426static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3427 RectangleInfo *offset,ExceptionInfo *exception)
3446 status = MagickTrue;
3449 minima_info = { MagickMaximumValue, 0, 0 };
3457 image_view=AcquireVirtualCacheView(image,exception);
3458 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3460 if (q != (
const Quantum *) NULL)
3461 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3462#if defined(MAGICKCORE_OPENMP_SUPPORT)
3463 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3464 magick_number_threads(image,image,image->rows,1)
3466 for (y=0; y < (ssize_t) image->rows; y++)
3472 channel_minima = { MagickMaximumValue, 0, 0 };
3477 if (status == MagickFalse)
3479 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3480 if (p == (
const Quantum *) NULL)
3485 channel_minima=minima_info;
3486 for (x=0; x < (ssize_t) image->columns; x++)
3491 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3496 PixelChannel channel = GetPixelChannelChannel(image,i);
3497 PixelTrait traits = GetPixelChannelTraits(image,channel);
3498 if ((traits & UpdatePixelTrait) == 0)
3500 pixel=(double) p[i];
3501 if (IsNaN(pixel) != 0)
3503 if (pixel < channel_minima.minima)
3505 channel_minima.minima=pixel;
3510 p+=(ptrdiff_t) GetPixelChannels(image);
3512#if defined(MAGICKCORE_OPENMP_SUPPORT)
3513 #pragma omp critical (MagickCore_SIMMinimaImage)
3515 if (channel_minima.minima < minima_info.minima)
3516 minima_info=channel_minima;
3518 image_view=DestroyCacheView(image_view);
3519 *minima=minima_info.minima;
3520 offset->x=minima_info.x;
3521 offset->y=minima_info.y;
3525static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
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 if (channel_statistics != (
const ChannelStatistics *) NULL)
3573 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3574 channel_statistics[channel].standard_deviation);
3576 q[i]=(Quantum) (factor*q[i]);
3578 q+=(ptrdiff_t) GetPixelChannels(image);
3580 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3583 image_view=DestroyCacheView(image_view);
3587static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3588 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3591 *alpha_fft = (Image *) NULL,
3592 *beta_fft = (Image *) NULL,
3593 *complex_multiplication = (Image *) NULL,
3594 *cross_correlation = (Image *) NULL;
3599 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3600 if (beta_fft == NULL)
3601 return((Image *) NULL);
3602 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3603 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3604 if (beta_fft == NULL)
3605 return((Image *) NULL);
3609 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3610 if (alpha_fft == (Image *) NULL)
3612 beta_fft=DestroyImageList(beta_fft);
3613 return((Image *) NULL);
3615 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3616 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3617 if (alpha_fft == (Image *) NULL)
3619 beta_fft=DestroyImageList(beta_fft);
3620 return((Image *) NULL);
3625 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3626 if (beta_fft == (Image *) NULL)
3628 alpha_fft=DestroyImageList(alpha_fft);
3629 return((Image *) NULL);
3634 AppendImageToList(&beta_fft,alpha_fft);
3635 DisableCompositeClampUnlessSpecified(beta_fft);
3636 DisableCompositeClampUnlessSpecified(beta_fft->next);
3637 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3639 beta_fft=DestroyImageList(beta_fft);
3640 if (complex_multiplication == (Image *) NULL)
3641 return((Image *) NULL);
3645 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3646 magnitude_image,0,0,exception);
3650 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3651 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3652 complex_multiplication->next,MagickFalse,exception);
3653 complex_multiplication=DestroyImageList(complex_multiplication);
3654 return(cross_correlation);
3657static MagickBooleanType SIMSetImageMean(Image *image,
3658 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3664 status = MagickTrue;
3672 image_view=AcquireAuthenticCacheView(image,exception);
3673#if defined(MAGICKCORE_OPENMP_SUPPORT)
3674 #pragma omp parallel for schedule(static) shared(status) \
3675 magick_number_threads(image,image,image->rows,1)
3677 for (y=0; y < (ssize_t) image->rows; y++)
3685 if (status == MagickFalse)
3687 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3688 if (q == (Quantum *) NULL)
3693 for (x=0; x < (ssize_t) image->columns; x++)
3698 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3700 PixelChannel channel = GetPixelChannelChannel(image,i);
3701 PixelTrait traits = GetPixelChannelTraits(image,channel);
3702 if ((traits & UpdatePixelTrait) == 0)
3704 q[i]=(Quantum) channel_statistics[channel].mean;
3706 q+=(ptrdiff_t) GetPixelChannels(image);
3708 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3711 image_view=DestroyCacheView(image_view);
3715static Image *SIMSubtractImageMean(
const Image *alpha_image,
3716 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3717 ExceptionInfo *exception)
3727 status = MagickTrue;
3735 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3736 MagickTrue,exception);
3737 if (subtract_image == (Image *) NULL)
3738 return(subtract_image);
3739 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3740 beta_view=AcquireVirtualCacheView(beta_image,exception);
3741#if defined(MAGICKCORE_OPENMP_SUPPORT)
3742 #pragma omp parallel for schedule(static) shared(status) \
3743 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3745 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3756 if (status == MagickFalse)
3758 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3759 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3761 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3766 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3771 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3773 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3774 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3775 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3776 if (((traits & UpdatePixelTrait) == 0) ||
3777 ((beta_traits & UpdatePixelTrait) == 0))
3779 if ((x >= (ssize_t) beta_image->columns) ||
3780 (y >= (ssize_t) beta_image->rows))
3783 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3784 channel_statistics[channel].mean);
3786 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3787 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3789 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3792 beta_view=DestroyCacheView(beta_view);
3793 image_view=DestroyCacheView(image_view);
3794 if (status == MagickFalse)
3795 subtract_image=DestroyImage(subtract_image);
3796 return(subtract_image);
3799static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3800 ExceptionInfo *exception)
3809 status = MagickTrue;
3817 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3818 MagickTrue,exception);
3819 if (unity_image == (Image *) NULL)
3820 return(unity_image);
3821 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3822 return(DestroyImage(unity_image));
3823 image_view=AcquireAuthenticCacheView(unity_image,exception);
3824#if defined(MAGICKCORE_OPENMP_SUPPORT)
3825 #pragma omp parallel for schedule(static) shared(status) \
3826 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3828 for (y=0; y < (ssize_t) unity_image->rows; y++)
3836 if (status == MagickFalse)
3838 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3840 if (q == (Quantum *) NULL)
3845 for (x=0; x < (ssize_t) unity_image->columns; x++)
3850 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3852 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3853 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3854 if ((traits & UpdatePixelTrait) == 0)
3856 if ((x >= (ssize_t) beta_image->columns) ||
3857 (y >= (ssize_t) beta_image->rows))
3862 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3864 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3867 image_view=DestroyCacheView(image_view);
3868 if (status == MagickFalse)
3869 unity_image=DestroyImage(unity_image);
3870 return(unity_image);
3873static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3874 ExceptionInfo *exception)
3884 status = MagickTrue;
3892 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3893 if (variance_image == (Image *) NULL)
3894 return(variance_image);
3895 image_view=AcquireAuthenticCacheView(variance_image,exception);
3896 beta_view=AcquireVirtualCacheView(beta_image,exception);
3897#if defined(MAGICKCORE_OPENMP_SUPPORT)
3898 #pragma omp parallel for schedule(static) shared(status) \
3899 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3901 for (y=0; y < (ssize_t) variance_image->rows; y++)
3912 if (status == MagickFalse)
3914 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3916 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3918 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3923 for (x=0; x < (ssize_t) variance_image->columns; x++)
3928 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3933 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3934 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3935 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3936 if (((traits & UpdatePixelTrait) == 0) ||
3937 ((beta_traits & UpdatePixelTrait) == 0))
3939 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3940 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3941 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3943 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3944 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3946 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3949 beta_view=DestroyCacheView(beta_view);
3950 image_view=DestroyCacheView(image_view);
3951 if (status == MagickFalse)
3952 variance_image=DestroyImage(variance_image);
3953 return(variance_image);
3956static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3957 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3959#define ThrowDPCSimilarityException() \
3961 if (dot_product_image != (Image *) NULL) \
3962 dot_product_image=DestroyImage(dot_product_image); \
3963 if (magnitude_image != (Image *) NULL) \
3964 magnitude_image=DestroyImage(magnitude_image); \
3965 if (reconstruct_image != (Image *) NULL) \
3966 reconstruct_image=DestroyImage(reconstruct_image); \
3967 if (rx_image != (Image *) NULL) \
3968 rx_image=DestroyImage(rx_image); \
3969 if (ry_image != (Image *) NULL) \
3970 ry_image=DestroyImage(ry_image); \
3971 if (target_image != (Image *) NULL) \
3972 target_image=DestroyImage(target_image); \
3973 if (threshold_image != (Image *) NULL) \
3974 threshold_image=DestroyImage(threshold_image); \
3975 if (trx_image != (Image *) NULL) \
3976 trx_image=DestroyImage(trx_image); \
3977 if (try_image != (Image *) NULL) \
3978 try_image=DestroyImage(try_image); \
3979 if (tx_image != (Image *) NULL) \
3980 tx_image=DestroyImage(tx_image); \
3981 if (ty_image != (Image *) NULL) \
3982 ty_image=DestroyImage(ty_image); \
3983 return((Image *) NULL); \
3990 standard_deviation = 0.0;
3993 *dot_product_image = (Image *) NULL,
3994 *magnitude_image = (Image *) NULL,
3995 *reconstruct_image = (Image *) NULL,
3996 *rx_image = (Image *) NULL,
3997 *ry_image = (Image *) NULL,
3998 *trx_image = (Image *) NULL,
3999 *target_image = (Image *) NULL,
4000 *threshold_image = (Image *) NULL,
4001 *try_image = (Image *) NULL,
4002 *tx_image = (Image *) NULL,
4003 *ty_image = (Image *) NULL;
4006 status = MagickTrue;
4014 target_image=CloneImage(image,0,0,MagickTrue,exception);
4015 if (target_image == (Image *) NULL)
4016 return((Image *) NULL);
4020 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4021 if (reconstruct_image == (Image *) NULL)
4022 ThrowDPCSimilarityException();
4026 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
4028 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
4029 if (rx_image == (Image *) NULL)
4030 ThrowDPCSimilarityException();
4031 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
4032 reconstruct_image=DestroyImage(reconstruct_image);
4033 if (ry_image == (Image *) NULL)
4034 ThrowDPCSimilarityException();
4038 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
4039 if (magnitude_image == (Image *) NULL)
4040 ThrowDPCSimilarityException();
4044 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
4045 if (threshold_image == (Image *) NULL)
4046 ThrowDPCSimilarityException();
4047 status=BilevelImage(threshold_image,0.0,exception);
4048 if (status == MagickFalse)
4049 ThrowDPCSimilarityException();
4050 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
4051 threshold_image=DestroyImage(threshold_image);
4052 if (status == MagickFalse)
4053 ThrowDPCSimilarityException();
4054 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
4055 reconstruct->rows)+QuantumScale;
4059 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
4060 rx_image=DestroyImage(rx_image);
4061 if (trx_image == (Image *) NULL)
4062 ThrowDPCSimilarityException();
4064 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
4065 magnitude_image=DestroyImage(magnitude_image);
4066 ry_image=DestroyImage(ry_image);
4067 if (try_image == (Image *) NULL)
4068 ThrowDPCSimilarityException();
4073 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
4075 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
4076 if (tx_image == (Image *) NULL)
4077 ThrowDPCSimilarityException();
4078 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
4079 target_image=DestroyImage(target_image);
4080 if (ty_image == (Image *) NULL)
4081 ThrowDPCSimilarityException();
4085 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
4086 if (magnitude_image == (Image *) NULL)
4087 ThrowDPCSimilarityException();
4091 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
4092 tx_image=DestroyImage(tx_image);
4093 if (trx_image == (Image *) NULL)
4094 ThrowDPCSimilarityException();
4096 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
4097 ty_image=DestroyImage(ty_image);
4098 magnitude_image=DestroyImage(magnitude_image);
4099 if (try_image == (Image *) NULL)
4100 ThrowDPCSimilarityException();
4105 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
4106 rx_image=DestroyImage(rx_image);
4107 tx_image=DestroyImage(tx_image);
4108 if (trx_image == (Image *) NULL)
4109 ThrowDPCSimilarityException();
4110 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
4111 ry_image=DestroyImage(ry_image);
4112 ty_image=DestroyImage(ty_image);
4113 if (try_image == (Image *) NULL)
4114 ThrowDPCSimilarityException();
4118 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
4119 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
4121 try_image=DestroyImage(try_image);
4122 if (status == MagickFalse)
4123 ThrowDPCSimilarityException();
4124 status=SIMMultiplyImage(trx_image,edge_factor,
4125 (
const ChannelStatistics *) NULL,exception);
4126 if (status == MagickFalse)
4127 ThrowDPCSimilarityException();
4131 SetGeometry(image,&geometry);
4132 geometry.width=image->columns;
4133 geometry.height=image->rows;
4134 (void) ResetImagePage(trx_image,
"0x0+0+0");
4135 dot_product_image=CropImage(trx_image,&geometry,exception);
4136 trx_image=DestroyImage(trx_image);
4137 if (dot_product_image == (Image *) NULL)
4138 ThrowDPCSimilarityException();
4139 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4143 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4145 if (status == MagickFalse)
4146 ThrowDPCSimilarityException();
4147 dot_product_image->depth=32;
4148 dot_product_image->colorspace=GRAYColorspace;
4149 dot_product_image->alpha_trait=UndefinedPixelTrait;
4150 status=SIMFilterImageNaNs(dot_product_image,exception);
4151 if (status == MagickFalse)
4152 ThrowDPCSimilarityException();
4153 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4154 if (status == MagickFalse)
4155 ThrowDPCSimilarityException();
4156 if ((QuantumScale*maxima) > 1.0)
4158 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4159 (
const ChannelStatistics *) NULL,exception);
4160 maxima=(double) QuantumRange;
4162 *similarity_metric=QuantumScale*maxima;
4163 return(dot_product_image);
4166static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4167 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4169#define ThrowMSESimilarityException() \
4171 if (alpha_image != (Image *) NULL) \
4172 alpha_image=DestroyImage(alpha_image); \
4173 if (beta_image != (Image *) NULL) \
4174 beta_image=DestroyImage(beta_image); \
4175 if (channel_statistics != (ChannelStatistics *) NULL) \
4176 channel_statistics=(ChannelStatistics *) \
4177 RelinquishMagickMemory(channel_statistics); \
4178 if (mean_image != (Image *) NULL) \
4179 mean_image=DestroyImage(mean_image); \
4180 if (mse_image != (Image *) NULL) \
4181 mse_image=DestroyImage(mse_image); \
4182 if (reconstruct_image != (Image *) NULL) \
4183 reconstruct_image=DestroyImage(reconstruct_image); \
4184 if (sum_image != (Image *) NULL) \
4185 sum_image=DestroyImage(sum_image); \
4186 if (alpha_image != (Image *) NULL) \
4187 alpha_image=DestroyImage(alpha_image); \
4188 return((Image *) NULL); \
4192 *channel_statistics = (ChannelStatistics *) NULL;
4198 *alpha_image = (Image *) NULL,
4199 *beta_image = (Image *) NULL,
4200 *mean_image = (Image *) NULL,
4201 *mse_image = (Image *) NULL,
4202 *reconstruct_image = (Image *) NULL,
4203 *sum_image = (Image *) NULL,
4204 *target_image = (Image *) NULL;
4207 status = MagickTrue;
4215 target_image=SIMSquareImage(image,exception);
4216 if (target_image == (Image *) NULL)
4217 ThrowMSESimilarityException();
4218 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4219 if (reconstruct_image == (Image *) NULL)
4220 ThrowMSESimilarityException();
4224 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4226 target_image=DestroyImage(target_image);
4227 if (alpha_image == (Image *) NULL)
4228 ThrowMSESimilarityException();
4229 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4230 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4231 if (status == MagickFalse)
4232 ThrowMSESimilarityException();
4236 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4237 MagickTrue,0,0,exception);
4238 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4239 if (beta_image == (Image *) NULL)
4241 reconstruct_image=DestroyImage(reconstruct_image);
4242 ThrowMSESimilarityException();
4244 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4245 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4246 reconstruct_image=DestroyImage(reconstruct_image);
4247 if (status == MagickFalse)
4248 ThrowMSESimilarityException();
4252 sum_image=SIMSquareImage(reconstruct,exception);
4253 if (sum_image == (Image *) NULL)
4254 ThrowMSESimilarityException();
4255 channel_statistics=GetImageStatistics(sum_image,exception);
4256 if (channel_statistics == (ChannelStatistics *) NULL)
4257 ThrowMSESimilarityException();
4258 status=SetImageStorageClass(sum_image,DirectClass,exception);
4259 if (status == MagickFalse)
4260 ThrowMSESimilarityException();
4261 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4262 channel_statistics=(ChannelStatistics *)
4263 RelinquishMagickMemory(channel_statistics);
4264 if (status == MagickFalse)
4265 ThrowMSESimilarityException();
4269 AppendImageToList(&sum_image,alpha_image);
4270 AppendImageToList(&sum_image,beta_image);
4271 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4272 if (mean_image == (Image *) NULL)
4273 ThrowMSESimilarityException();
4274 sum_image=DestroyImage(sum_image);
4275 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4276 if (status == MagickFalse)
4277 ThrowMSESimilarityException();
4281 SetGeometry(image,&geometry);
4282 geometry.width=image->columns;
4283 geometry.height=image->rows;
4284 (void) ResetImagePage(mean_image,
"0x0+0+0");
4285 mse_image=CropImage(mean_image,&geometry,exception);
4286 mean_image=DestroyImage(mean_image);
4287 if (mse_image == (Image *) NULL)
4288 ThrowMSESimilarityException();
4292 (void) ResetImagePage(mse_image,
"0x0+0+0");
4293 (void) ClampImage(mse_image,exception);
4294 mse_image->depth=32;
4295 mse_image->colorspace=GRAYColorspace;
4296 mse_image->alpha_trait=UndefinedPixelTrait;
4297 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4298 if (status == MagickFalse)
4299 ThrowMSESimilarityException();
4300 status=NegateImage(mse_image,MagickFalse,exception);
4301 if (status == MagickFalse)
4302 ThrowMSESimilarityException();
4303 alpha_image=DestroyImage(alpha_image);
4304 beta_image=DestroyImage(beta_image);
4305 if ((QuantumScale*minima) < FLT_EPSILON)
4307 *similarity_metric=QuantumScale*minima;
4311static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4312 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4314#define ThrowNCCSimilarityException() \
4316 if (alpha_image != (Image *) NULL) \
4317 alpha_image=DestroyImage(alpha_image); \
4318 if (beta_image != (Image *) NULL) \
4319 beta_image=DestroyImage(beta_image); \
4320 if (channel_statistics != (ChannelStatistics *) NULL) \
4321 channel_statistics=(ChannelStatistics *) \
4322 RelinquishMagickMemory(channel_statistics); \
4323 if (correlation_image != (Image *) NULL) \
4324 correlation_image=DestroyImage(correlation_image); \
4325 if (divide_image != (Image *) NULL) \
4326 divide_image=DestroyImage(divide_image); \
4327 if (ncc_image != (Image *) NULL) \
4328 ncc_image=DestroyImage(ncc_image); \
4329 if (normalize_image != (Image *) NULL) \
4330 normalize_image=DestroyImage(normalize_image); \
4331 if (reconstruct_image != (Image *) NULL) \
4332 reconstruct_image=DestroyImage(reconstruct_image); \
4333 if (target_image != (Image *) NULL) \
4334 target_image=DestroyImage(target_image); \
4335 if (variance_image != (Image *) NULL) \
4336 variance_image=DestroyImage(variance_image); \
4337 return((Image *) NULL); \
4341 *channel_statistics = (ChannelStatistics *) NULL;
4347 *alpha_image = (Image *) NULL,
4348 *beta_image = (Image *) NULL,
4349 *correlation_image = (Image *) NULL,
4350 *divide_image = (Image *) NULL,
4351 *ncc_image = (Image *) NULL,
4352 *normalize_image = (Image *) NULL,
4353 *reconstruct_image = (Image *) NULL,
4354 *target_image = (Image *) NULL,
4355 *variance_image = (Image *) NULL;
4358 status = MagickTrue;
4366 target_image=SIMSquareImage(image,exception);
4367 if (target_image == (Image *) NULL)
4368 ThrowNCCSimilarityException();
4369 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4370 if (reconstruct_image == (Image *) NULL)
4371 ThrowNCCSimilarityException();
4375 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4377 target_image=DestroyImage(target_image);
4378 if (alpha_image == (Image *) NULL)
4379 ThrowNCCSimilarityException();
4380 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4381 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4383 if (status == MagickFalse)
4384 ThrowNCCSimilarityException();
4388 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4389 reconstruct_image=DestroyImage(reconstruct_image);
4390 if (beta_image == (Image *) NULL)
4391 ThrowNCCSimilarityException();
4392 target_image=SIMSquareImage(beta_image,exception);
4393 beta_image=DestroyImage(beta_image);
4394 if (target_image == (Image *) NULL)
4395 ThrowNCCSimilarityException();
4396 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4397 (
const ChannelStatistics *) NULL,exception);
4398 if (status == MagickFalse)
4399 ThrowNCCSimilarityException();
4403 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4404 target_image=DestroyImage(target_image);
4405 alpha_image=DestroyImage(alpha_image);
4406 if (variance_image == (Image *) NULL)
4407 ThrowNCCSimilarityException();
4411 channel_statistics=GetImageStatistics(reconstruct,exception);
4412 if (channel_statistics == (ChannelStatistics *) NULL)
4413 ThrowNCCSimilarityException();
4414 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4415 if (status == MagickFalse)
4416 ThrowNCCSimilarityException();
4417 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4419 channel_statistics=(ChannelStatistics *)
4420 RelinquishMagickMemory(channel_statistics);
4421 if (normalize_image == (Image *) NULL)
4422 ThrowNCCSimilarityException();
4423 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4424 normalize_image=DestroyImage(normalize_image);
4425 if (correlation_image == (Image *) NULL)
4426 ThrowNCCSimilarityException();
4430 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4431 correlation_image=DestroyImage(correlation_image);
4432 variance_image=DestroyImage(variance_image);
4433 if (divide_image == (Image *) NULL)
4434 ThrowNCCSimilarityException();
4438 SetGeometry(image,&geometry);
4439 geometry.width=image->columns;
4440 geometry.height=image->rows;
4441 (void) ResetImagePage(divide_image,
"0x0+0+0");
4442 ncc_image=CropImage(divide_image,&geometry,exception);
4443 divide_image=DestroyImage(divide_image);
4444 if (ncc_image == (Image *) NULL)
4445 ThrowNCCSimilarityException();
4449 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4450 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4451 if (status == MagickFalse)
4452 ThrowNCCSimilarityException();
4453 ncc_image->depth=32;
4454 ncc_image->colorspace=GRAYColorspace;
4455 ncc_image->alpha_trait=UndefinedPixelTrait;
4456 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4457 if (status == MagickFalse)
4458 ThrowNCCSimilarityException();
4459 if ((QuantumScale*maxima) > 1.0)
4461 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4462 (
const ChannelStatistics *) NULL,exception);
4463 maxima=(double) QuantumRange;
4465 *similarity_metric=QuantumScale*maxima;
4469static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4470 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4472#define ThrowPhaseSimilarityException() \
4474 if (correlation_image != (Image *) NULL) \
4475 correlation_image=DestroyImage(correlation_image); \
4476 if (fft_images != (Image *) NULL) \
4477 fft_images=DestroyImageList(fft_images); \
4478 if (gamma_image != (Image *) NULL) \
4479 gamma_image=DestroyImage(gamma_image); \
4480 if (magnitude_image != (Image *) NULL) \
4481 magnitude_image=DestroyImage(magnitude_image); \
4482 if (phase_image != (Image *) NULL) \
4483 phase_image=DestroyImage(phase_image); \
4484 if (reconstruct_image != (Image *) NULL) \
4485 reconstruct_image=DestroyImage(reconstruct_image); \
4486 if (reconstruct_magnitude != (Image *) NULL) \
4487 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4488 if (target_image != (Image *) NULL) \
4489 target_image=DestroyImage(target_image); \
4490 if (test_magnitude != (Image *) NULL) \
4491 test_magnitude=DestroyImage(test_magnitude); \
4492 return((Image *) NULL); \
4499 *correlation_image = (Image *) NULL,
4500 *fft_images = (Image *) NULL,
4501 *gamma_image = (Image *) NULL,
4502 *magnitude_image = (Image *) NULL,
4503 *phase_image = (Image *) NULL,
4504 *reconstruct_image = (Image *) NULL,
4505 *reconstruct_magnitude = (Image *) NULL,
4506 *target_image = (Image *) NULL,
4507 *test_magnitude = (Image *) NULL;
4510 status = MagickTrue;
4518 target_image=CloneImage(image,0,0,MagickTrue,exception);
4519 if (target_image == (Image *) NULL)
4520 ThrowPhaseSimilarityException();
4521 (void) ResetImagePage(target_image,
"0x0+0+0");
4522 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4523 &target_image->background_color);
4524 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4525 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4526 if (status == MagickFalse)
4527 ThrowPhaseSimilarityException();
4531 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4532 if (reconstruct_image == (Image *) NULL)
4533 ThrowPhaseSimilarityException();
4534 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4535 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4536 &reconstruct_image->background_color);
4537 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4538 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4539 if (status == MagickFalse)
4540 ThrowPhaseSimilarityException();
4544 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4545 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4546 if (fft_images == (Image *) NULL)
4547 ThrowPhaseSimilarityException();
4548 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4549 fft_images=DestroyImageList(fft_images);
4550 if (test_magnitude == (Image *) NULL)
4551 ThrowPhaseSimilarityException();
4552 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4553 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4555 if (fft_images == (Image *) NULL)
4556 ThrowPhaseSimilarityException();
4557 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4558 fft_images=DestroyImageList(fft_images);
4559 if (reconstruct_magnitude == (Image *) NULL)
4560 ThrowPhaseSimilarityException();
4561 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4562 if (magnitude_image == (Image *) NULL)
4563 ThrowPhaseSimilarityException();
4564 DisableCompositeClampUnlessSpecified(magnitude_image);
4565 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4566 MagickTrue,0,0,exception);
4570 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4571 magnitude_image,exception);
4572 target_image=DestroyImage(target_image);
4573 reconstruct_image=DestroyImage(reconstruct_image);
4574 test_magnitude=DestroyImage(test_magnitude);
4575 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4576 if (correlation_image == (Image *) NULL)
4577 ThrowPhaseSimilarityException();
4581 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4582 correlation_image=DestroyImage(correlation_image);
4583 if (gamma_image == (Image *) NULL)
4584 ThrowPhaseSimilarityException();
4588 SetGeometry(image,&geometry);
4589 geometry.width=image->columns;
4590 geometry.height=image->rows;
4591 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4592 phase_image=CropImage(gamma_image,&geometry,exception);
4593 gamma_image=DestroyImage(gamma_image);
4594 if (phase_image == (Image *) NULL)
4595 ThrowPhaseSimilarityException();
4596 (void) ResetImagePage(phase_image,
"0x0+0+0");
4600 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4601 if (status == MagickFalse)
4602 ThrowPhaseSimilarityException();
4603 phase_image->depth=32;
4604 phase_image->colorspace=GRAYColorspace;
4605 phase_image->alpha_trait=UndefinedPixelTrait;
4606 status=SIMFilterImageNaNs(phase_image,exception);
4607 if (status == MagickFalse)
4608 ThrowPhaseSimilarityException();
4609 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4610 if (status == MagickFalse)
4611 ThrowPhaseSimilarityException();
4612 magnitude_image=DestroyImage(magnitude_image);
4613 if ((QuantumScale*maxima) > 1.0)
4615 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4616 (
const ChannelStatistics *) NULL,exception);
4617 maxima=(double) QuantumRange;
4619 *similarity_metric=QuantumScale*maxima;
4620 return(phase_image);
4623static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4624 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4627 *psnr_image = (Image *) NULL;
4629 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4631 if (psnr_image == (Image *) NULL)
4633 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4634 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4638static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4639 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4642 *rmse_image = (Image *) NULL;
4644 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4646 if (rmse_image == (Image *) NULL)
4648 *similarity_metric=sqrt(*similarity_metric);
4653static double GetSimilarityMetric(
const Image *image,
4654 const Image *reconstruct_image,
const MetricType metric,
4655 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4658 *channel_similarity,
4662 *sans_exception = AcquireExceptionInfo();
4668 status = MagickTrue;
4674 length = MaxPixelChannels+1UL;
4676 SetGeometry(reconstruct_image,&geometry);
4677 geometry.x=x_offset;
4678 geometry.y=y_offset;
4679 similarity_image=CropImage(image,&geometry,sans_exception);
4680 sans_exception=DestroyExceptionInfo(sans_exception);
4681 if (similarity_image == (Image *) NULL)
4686 channel_similarity=(
double *) AcquireQuantumMemory(length,
4687 sizeof(*channel_similarity));
4688 if (channel_similarity == (
double *) NULL)
4689 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4690 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4693 case AbsoluteErrorMetric:
4695 status=GetAESimilarity(similarity_image,reconstruct_image,
4696 channel_similarity,exception);
4699 case DotProductCorrelationErrorMetric:
4701 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4702 channel_similarity,exception);
4705 case FuzzErrorMetric:
4707 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4708 channel_similarity,exception);
4711 case MeanAbsoluteErrorMetric:
4713 status=GetMAESimilarity(similarity_image,reconstruct_image,
4714 channel_similarity,exception);
4717 case MeanErrorPerPixelErrorMetric:
4719 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4720 channel_similarity,exception);
4723 case MeanSquaredErrorMetric:
4725 status=GetMSESimilarity(similarity_image,reconstruct_image,
4726 channel_similarity,exception);
4729 case NormalizedCrossCorrelationErrorMetric:
4731 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4732 channel_similarity,exception);
4735 case PeakAbsoluteErrorMetric:
4737 status=GetPASimilarity(similarity_image,reconstruct_image,
4738 channel_similarity,exception);
4741 case PeakSignalToNoiseRatioErrorMetric:
4743 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4744 channel_similarity,exception);
4747 case PerceptualHashErrorMetric:
4749 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4750 channel_similarity,exception);
4753 case PhaseCorrelationErrorMetric:
4755 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4756 channel_similarity,exception);
4759 case PixelDifferenceCountErrorMetric:
4761 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4762 channel_similarity,exception);
4765 case RootMeanSquaredErrorMetric:
4766 case UndefinedErrorMetric:
4769 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4770 channel_similarity,exception);
4773 case StructuralDissimilarityErrorMetric:
4775 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4776 channel_similarity,exception);
4779 case StructuralSimilarityErrorMetric:
4781 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4782 channel_similarity,exception);
4786 similarity_image=DestroyImage(similarity_image);
4787 similarity=channel_similarity[CompositePixelChannel];
4788 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4789 if (status == MagickFalse)
4794MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4795 const MetricType metric,
const double similarity_threshold,
4796 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4798#define SimilarityImageTag "Similarity/Image"
4814 *similarity_image = (Image *) NULL;
4817 status = MagickTrue;
4823 similarity_info = { 0.0, 0, 0 };
4832 assert(image != (
const Image *) NULL);
4833 assert(image->signature == MagickCoreSignature);
4834 assert(exception != (ExceptionInfo *) NULL);
4835 assert(exception->signature == MagickCoreSignature);
4836 assert(offset != (RectangleInfo *) NULL);
4837 if (IsEventLogging() != MagickFalse)
4838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4839 SetGeometry(reconstruct,offset);
4840 *similarity_metric=0.0;
4843#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4845 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4846 if (artifact == (
const char *) NULL)
4847 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4848 if (((artifact == (
const char *) NULL) ||
4849 (IsStringTrue(artifact) != MagickFalse)) &&
4850 ((image->channels & ReadMaskChannel) == 0))
4853 case DotProductCorrelationErrorMetric:
4855 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4856 similarity_metric,exception);
4857 return(similarity_image);
4859 case MeanSquaredErrorMetric:
4861 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4862 similarity_metric,exception);
4863 return(similarity_image);
4865 case NormalizedCrossCorrelationErrorMetric:
4867 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4868 similarity_metric,exception);
4869 return(similarity_image);
4871 case PeakSignalToNoiseRatioErrorMetric:
4873 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4874 similarity_metric,exception);
4875 return(similarity_image);
4877 case PhaseCorrelationErrorMetric:
4879 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4880 similarity_metric,exception);
4881 return(similarity_image);
4883 case RootMeanSquaredErrorMetric:
4884 case UndefinedErrorMetric:
4886 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4887 similarity_metric,exception);
4888 return(similarity_image);
4895 if ((image->columns < reconstruct->columns) ||
4896 (image->rows < reconstruct->rows))
4898 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4899 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4900 return((Image *) NULL);
4902 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4903 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4904 if (similarity_image == (Image *) NULL)
4905 return((Image *) NULL);
4906 similarity_image->depth=32;
4907 similarity_image->colorspace=GRAYColorspace;
4908 similarity_image->alpha_trait=UndefinedPixelTrait;
4909 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4910 if (status == MagickFalse)
4911 return(DestroyImage(similarity_image));
4915 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4916 similarity_info.x,similarity_info.y,exception);
4917 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4918#if defined(MAGICKCORE_OPENMP_SUPPORT)
4919 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4920 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4922 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4928 threshold_trigger = MagickFalse;
4934 channel_info = similarity_info;
4939 if (status == MagickFalse)
4941 if (threshold_trigger != MagickFalse)
4943 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4944 similarity_image->columns,1,exception);
4945 if (q == (Quantum *) NULL)
4950 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4955 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4959 case DotProductCorrelationErrorMetric:
4960 case NormalizedCrossCorrelationErrorMetric:
4961 case PeakSignalToNoiseRatioErrorMetric:
4962 case PhaseCorrelationErrorMetric:
4963 case StructuralSimilarityErrorMetric:
4965 if (similarity <= channel_info.similarity)
4967 channel_info.similarity=similarity;
4974 if (similarity >= channel_info.similarity)
4976 channel_info.similarity=similarity;
4982 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4984 PixelChannel channel = GetPixelChannelChannel(image,i);
4985 PixelTrait traits = GetPixelChannelTraits(image,channel);
4986 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4988 if (((traits & UpdatePixelTrait) == 0) ||
4989 ((similarity_traits & UpdatePixelTrait) == 0))
4993 case DotProductCorrelationErrorMetric:
4994 case NormalizedCrossCorrelationErrorMetric:
4995 case PeakSignalToNoiseRatioErrorMetric:
4996 case PhaseCorrelationErrorMetric:
4997 case StructuralSimilarityErrorMetric:
4999 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
5000 QuantumRange*similarity),q);
5005 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
5006 QuantumRange*(1.0-similarity)),q);
5011 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
5013#if defined(MAGICKCORE_OPENMP_SUPPORT)
5014 #pragma omp critical (MagickCore_GetSimilarityMetric)
5018 case DotProductCorrelationErrorMetric:
5019 case NormalizedCrossCorrelationErrorMetric:
5020 case PeakSignalToNoiseRatioErrorMetric:
5021 case PhaseCorrelationErrorMetric:
5022 case StructuralSimilarityErrorMetric:
5024 if (similarity_threshold != DefaultSimilarityThreshold)
5025 if (channel_info.similarity >= similarity_threshold)
5026 threshold_trigger=MagickTrue;
5027 if (channel_info.similarity >= similarity_info.similarity)
5028 similarity_info=channel_info;
5033 if (similarity_threshold != DefaultSimilarityThreshold)
5034 if (channel_info.similarity < similarity_threshold)
5035 threshold_trigger=MagickTrue;
5036 if (channel_info.similarity < similarity_info.similarity)
5037 similarity_info=channel_info;
5041 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
5043 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5049 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
5050 if (proceed == MagickFalse)
5054 similarity_view=DestroyCacheView(similarity_view);
5055 if (status == MagickFalse)
5056 similarity_image=DestroyImage(similarity_image);
5057 *similarity_metric=similarity_info.similarity;
5058 if (fabs(*similarity_metric) < MagickEpsilon)
5059 *similarity_metric=0.0;
5060 offset->x=similarity_info.x;
5061 offset->y=similarity_info.y;
5062 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
5063 GetMagickPrecision(),*similarity_metric);
5064 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
5065 GetMagickPrecision(),(
double) offset->x);
5066 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
5067 GetMagickPrecision(),(
double) offset->y);
5068 return(similarity_image);