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(columns,rows,channels) \
1656 (((size_t) (columns) != 0) && ((size_t) (rows) <= SIZE_MAX / (size_t) (columns)) && \
1657 ((size_t) (channels) <= SIZE_MAX / ((size_t) (columns) * (size_t) (rows))))
1683 if (HeapOverflowCheck(GetPixelChannels(image),image->rows,2) == MagickFalse)
1684 return((MemoryInfo *) NULL);
1685 if (HeapOverflowCheck(rows,columns,GetPixelChannels(image)) == MagickFalse)
1686 return((MemoryInfo *) NULL);
1687 number_gradients=(size_t) 2*GetPixelChannels(image)*image->rows;
1688 gradient=(
double *) AcquireQuantumMemory(number_gradients,
sizeof(*gradient));
1689 number_phases=(size_t) rows*columns*GetPixelChannels(image);
1690 phase_info=AcquireVirtualMemory(number_phases,
sizeof(*phase));
1691 if ((gradient == (
double *) NULL) || (phase_info == (MemoryInfo *) NULL))
1693 if (gradient != (
double *) NULL)
1694 gradient=(
double *) RelinquishMagickMemory(gradient);
1695 if (phase_info != (MemoryInfo *) NULL)
1696 phase_info=RelinquishVirtualMemory(phase_info);
1697 return((MemoryInfo *) NULL);
1699 phase=(
double *) GetVirtualMemoryBlob(phase_info);
1700 (void) memset(phase,0,number_phases*
sizeof(*phase));
1702 image_view=AcquireVirtualCacheView(image,exception);
1703 for (u=0; u < (ssize_t) columns; u++)
1718 (void) memset(gradient,0,number_gradients*
sizeof(*gradient));
1719 theta_u=2.0*MagickPI*(double) u/(
double) image->rows;
1720 cosine=cos(theta_u);
1722 for (y=0; y < (ssize_t) image->rows; y++)
1734 if (status == MagickFalse)
1736 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1737 if (p == (
const Quantum *) NULL)
1744 for (x=0; x < (ssize_t) image->columns; x++)
1750 Sa=QuantumScale*(double) GetPixelAlpha(image,p);
1751 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1759 PixelChannel channel = GetPixelChannelChannel(image,i);
1760 PixelTrait traits = GetPixelChannelTraits(image,channel);
1761 if (traits == UndefinedPixelTrait)
1763 pixel=(channel == AlphaPixelChannel) ? QuantumScale*(
double) p[i] :
1764 QuantumScale*Sa*(double) p[i];
1765 j=2*((size_t) y*GetPixelChannels(image)+(size_t) i);
1766 gradient[j]+=pixel*cx;
1767 gradient[j+1]+=pixel*sx;
1773 cx=cx*cosine-sx*sine;
1774 sx=sx*cosine+tmp_cx*sine;
1775 p+=(ptrdiff_t) GetPixelChannels(image);
1778 if (status == MagickFalse)
1780 for (v=0; v < (ssize_t) rows; v++)
1783 channel_imag[MaxPixelChannels+1],
1784 channel_real[MaxPixelChannels+1],
1794 (void) memset(channel_real,0,
sizeof(channel_real));
1795 (void) memset(channel_imag,0,
sizeof(channel_imag));
1796 theta_v=2.0*MagickPI*(double) v/(
double) image->columns;
1797 cosine_v=cos(theta_v);
1798 sine_v=sin(theta_v);
1801 for (y=0; y < (ssize_t) image->rows; y++)
1806 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1811 PixelChannel channel = GetPixelChannelChannel(image,i);
1812 PixelTrait traits = GetPixelChannelTraits(image,channel);
1813 if (traits == UndefinedPixelTrait)
1815 j=2*((size_t) y*GetPixelChannels(image)+(size_t) i);
1816 channel_real[i]+=gradient[j]*cy-gradient[j+1]*sy;
1817 channel_imag[i]+=gradient[j+1]*cy+gradient[j]*sy;
1823 cy=cy*cosine_v-sy*sine_v;
1824 sy=sy*cosine_v+tmp_cy*sine_v;
1826 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1831 PixelChannel channel = GetPixelChannelChannel(image,i);
1832 PixelTrait traits = GetPixelChannelTraits(image,channel);
1833 if (traits == UndefinedPixelTrait)
1835 j=((size_t) v*columns+(size_t) u)*GetPixelChannels(image)+(size_t) i;
1836 phase[j]=atan2(channel_imag[i],channel_real[i]);
1840 image_view=DestroyCacheView(image_view);
1841 gradient=(
double *) RelinquishMagickMemory(gradient);
1842 if (status == MagickFalse)
1844 phase_info=RelinquishVirtualMemory(phase_info);
1845 return((MemoryInfo *) NULL);
1850static MagickBooleanType GetPHASESimilarity(
const Image *image,
1851 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
1860 *reconstruct_spectra;
1863 status = MagickTrue;
1880 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1881 phase_info=ComputeAllPhaseSpectra(image,rows,columns,exception);
1882 reconstruct_info=ComputeAllPhaseSpectra(reconstruct_image,rows,columns,
1884 if ((phase_info == (MemoryInfo *) NULL) ||
1885 (reconstruct_info == (MemoryInfo *) NULL))
1887 if (phase_info != (MemoryInfo *) NULL)
1888 phase_info=RelinquishVirtualMemory(phase_info);
1889 if (reconstruct_info != (MemoryInfo *) NULL)
1890 reconstruct_info=RelinquishVirtualMemory(reconstruct_info);
1891 return(MagickFalse);
1893 phase_spectra=(
double *) GetVirtualMemoryBlob(phase_info);
1894 reconstruct_spectra=(
double *) GetVirtualMemoryBlob(reconstruct_info);
1895 image_view=AcquireVirtualCacheView(image,exception);
1896 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
1897#if defined(MAGICKCORE_OPENMP_SUPPORT)
1898 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
1899 magick_number_threads(image,image,rows,0.25)
1901 for (y=0; y < (ssize_t) rows; y++)
1909 channel_similarity[MaxPixelChannels+1] = { 0.0 };
1914 if (status == MagickFalse)
1916 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
1917 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
1918 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
1923 for (x=0; x < (ssize_t) columns; x++)
1928 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
1929 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
1931 p+=(ptrdiff_t) GetPixelChannels(image);
1932 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1935 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1940 PixelChannel channel = GetPixelChannelChannel(image,i);
1941 PixelTrait traits = GetPixelChannelTraits(image,channel);
1942 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1944 if (((traits & UpdatePixelTrait) == 0) ||
1945 ((reconstruct_traits & UpdatePixelTrait) == 0))
1947 delta=(phase_spectra[((size_t) y*columns+(size_t) x)*
1948 GetPixelChannels(image)+(size_t) i])-((i < (ssize_t)
1949 GetPixelChannels(reconstruct_image)) ?
1950 reconstruct_spectra[((size_t) y*columns+(size_t) x)*
1951 GetPixelChannels(reconstruct_image)+(size_t) i] : 0.0);
1952 channel_similarity[i]+=cos(delta);
1953 channel_similarity[CompositePixelChannel]+=cos(delta);
1956 p+=(ptrdiff_t) GetPixelChannels(image);
1957 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
1959#if defined(MAGICKCORE_OPENMP_SUPPORT)
1960 #pragma omp critical (MagickCore_GetPHASESimilarity)
1967 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1969 PixelChannel channel = GetPixelChannelChannel(image,j);
1970 PixelTrait traits = GetPixelChannelTraits(image,channel);
1971 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1973 if (((traits & UpdatePixelTrait) == 0) ||
1974 ((reconstruct_traits & UpdatePixelTrait) == 0))
1976 similarity[j]+=channel_similarity[j];
1978 similarity[CompositePixelChannel]+=
1979 channel_similarity[CompositePixelChannel];
1982 reconstruct_view=DestroyCacheView(reconstruct_view);
1983 image_view=DestroyCacheView(image_view);
1984 phase_info=RelinquishVirtualMemory(phase_info);
1985 reconstruct_info=RelinquishVirtualMemory(reconstruct_info);
1986 area=MagickSafeReciprocal(area);
1987 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
1989 PixelChannel channel = GetPixelChannelChannel(image,k);
1990 PixelTrait traits = GetPixelChannelTraits(image,channel);
1991 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
1993 if (((traits & UpdatePixelTrait) == 0) ||
1994 ((reconstruct_traits & UpdatePixelTrait) == 0))
1996 similarity[k]*=area;
1998 similarity[CompositePixelChannel]*=area;
1999 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2003static MagickBooleanType GetPHASHSimilarity(
const Image *image,
2004 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2006 ChannelPerceptualHash
2019 channel_phash=GetImagePerceptualHash(image,exception);
2020 if (channel_phash == (ChannelPerceptualHash *) NULL)
2021 return(MagickFalse);
2022 reconstruct_phash=GetImagePerceptualHash(reconstruct_image,exception);
2023 if (reconstruct_phash == (ChannelPerceptualHash *) NULL)
2025 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
2027 return(MagickFalse);
2029 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2037 PixelChannel channel = GetPixelChannelChannel(image,i);
2038 PixelTrait traits = GetPixelChannelTraits(image,channel);
2039 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2041 if (((traits & UpdatePixelTrait) == 0) ||
2042 ((reconstruct_traits & UpdatePixelTrait) == 0))
2044 for (j=0; j < (ssize_t) channel_phash[0].number_colorspaces; j++)
2053 for (k=0; k < MaximumNumberOfPerceptualHashes; k++)
2058 alpha=channel_phash[i].phash[j][k];
2059 beta=reconstruct_phash[i].phash[j][k];
2061 if (IsNaN(error) != 0)
2063 difference+=error*error;
2066 similarity[i]+=difference;
2067 similarity[CompositePixelChannel]+=difference;
2069 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2070 artifact=GetImageArtifact(image,
"phash:normalize");
2071 if (IsStringTrue(artifact) != MagickFalse)
2073 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2075 PixelChannel channel = GetPixelChannelChannel(image,i);
2076 PixelTrait traits = GetPixelChannelTraits(image,channel);
2077 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2079 if (((traits & UpdatePixelTrait) == 0) ||
2080 ((reconstruct_traits & UpdatePixelTrait) == 0))
2082 similarity[i]=sqrt(similarity[i]/channel_phash[0].number_colorspaces);
2084 similarity[CompositePixelChannel]=sqrt(similarity[CompositePixelChannel]/
2085 channel_phash[0].number_colorspaces);
2090 reconstruct_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
2092 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(channel_phash);
2096static MagickBooleanType GetPSNRSimilarity(
const Image *image,
2097 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2100 status = MagickTrue;
2108 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2109 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2111 PixelChannel channel = GetPixelChannelChannel(image,i);
2112 PixelTrait traits = GetPixelChannelTraits(image,channel);
2113 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2115 if (((traits & UpdatePixelTrait) == 0) ||
2116 ((reconstruct_traits & UpdatePixelTrait) == 0))
2118 similarity[i]=10.0*MagickSafeLog10(MagickSafeReciprocal(
2119 similarity[i]))/MagickSafePSNRRecipicol(10.0);
2121 similarity[CompositePixelChannel]=10.0*MagickSafeLog10(
2122 MagickSafeReciprocal(similarity[CompositePixelChannel]))/
2123 MagickSafePSNRRecipicol(10.0);
2127static MagickBooleanType GetRMSESimilarity(
const Image *image,
2128 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2130#define RMSESquareRoot(x) sqrt((x) < 0.0 ? 0.0 : (x))
2133 status = MagickTrue;
2141 status=GetMSESimilarity(image,reconstruct_image,similarity,exception);
2142 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2144 PixelChannel channel = GetPixelChannelChannel(image,i);
2145 PixelTrait traits = GetPixelChannelTraits(image,channel);
2146 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2148 if (((traits & UpdatePixelTrait) == 0) ||
2149 ((reconstruct_traits & UpdatePixelTrait) == 0))
2151 similarity[i]=RMSESquareRoot(similarity[i]);
2153 similarity[CompositePixelChannel]=RMSESquareRoot(
2154 similarity[CompositePixelChannel]);
2158static MagickBooleanType GetSSIMSimularity(
const Image *image,
2159 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2161#define SSIMRadius 5.0
2162#define SSIMSigma 1.5
2172 geometry[MagickPathExtent];
2188 status = MagickTrue;
2202 artifact=GetImageArtifact(image,
"compare:ssim-radius");
2203 if (artifact != (
const char *) NULL)
2204 radius=StringToDouble(artifact,(
char **) NULL);
2206 artifact=GetImageArtifact(image,
"compare:ssim-sigma");
2207 if (artifact != (
const char *) NULL)
2208 sigma=StringToDouble(artifact,(
char **) NULL);
2209 (void) FormatLocaleString(geometry,MagickPathExtent,
"gaussian:%.17gx%.17g",
2211 kernel_info=AcquireKernelInfo(geometry,exception);
2212 if (kernel_info == (KernelInfo *) NULL)
2213 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2215 c1=pow(SSIMK1*SSIML,2.0);
2216 artifact=GetImageArtifact(image,
"compare:ssim-k1");
2217 if (artifact != (
const char *) NULL)
2218 c1=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2219 c2=pow(SSIMK2*SSIML,2.0);
2220 artifact=GetImageArtifact(image,
"compare:ssim-k2");
2221 if (artifact != (
const char *) NULL)
2222 c2=pow(StringToDouble(artifact,(
char **) NULL)*SSIML,2.0);
2223 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2224 image_view=AcquireVirtualCacheView(image,exception);
2225 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2226#if defined(MAGICKCORE_OPENMP_SUPPORT)
2227 #pragma omp parallel for schedule(static) shared(area,similarity,status) \
2228 magick_number_threads(image,reconstruct_image,rows,1)
2230 for (y=0; y < (ssize_t) rows; y++)
2238 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2244 if (status == MagickFalse)
2246 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
2247 ((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2248 kernel_info->height,exception);
2249 q=GetCacheViewVirtualPixels(reconstruct_view,-((ssize_t) kernel_info->width/
2250 2L),y-((ssize_t) kernel_info->height/2L),columns+kernel_info->width,
2251 kernel_info->height,exception);
2252 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2257 for (x=0; x < (ssize_t) columns; x++)
2260 *magick_restrict reconstruct,
2261 *magick_restrict test;
2264 x_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2265 x_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 },
2266 xy_sigma[MaxPixelChannels+1] = { 0.0 },
2267 y_pixel_mu[MaxPixelChannels+1] = { 0.0 },
2268 y_pixel_sigma_squared[MaxPixelChannels+1] = { 0.0 };
2276 if ((GetPixelReadMask(image,p) <= (QuantumRange/2)) ||
2277 (GetPixelReadMask(reconstruct_image,q) <= (QuantumRange/2)))
2279 p+=(ptrdiff_t) GetPixelChannels(image);
2280 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2283 k=kernel_info->values;
2286 for (v=0; v < (ssize_t) kernel_info->height; v++)
2291 for (u=0; u < (ssize_t) kernel_info->width; u++)
2293 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2299 PixelChannel channel = GetPixelChannelChannel(image,i);
2300 PixelTrait traits = GetPixelChannelTraits(image,channel);
2301 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2302 reconstruct_image,channel);
2303 if (((traits & UpdatePixelTrait) == 0) ||
2304 ((reconstruct_traits & UpdatePixelTrait) == 0))
2306 x_pixel=QuantumScale*(double) test[i];
2307 x_pixel_mu[i]+=(*k)*x_pixel;
2308 x_pixel_sigma_squared[i]+=(*k)*x_pixel*x_pixel;
2309 y_pixel=QuantumScale*(double)
2310 GetPixelChannel(reconstruct_image,channel,reconstruct);
2311 y_pixel_mu[i]+=(*k)*y_pixel;
2312 y_pixel_sigma_squared[i]+=(*k)*y_pixel*y_pixel;
2313 xy_sigma[i]+=(*k)*x_pixel*y_pixel;
2316 test+=(ptrdiff_t) GetPixelChannels(image);
2317 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2319 test+=(ptrdiff_t) GetPixelChannels(image)*columns;
2320 reconstruct+=(ptrdiff_t) GetPixelChannels(reconstruct_image)*columns;
2322 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2327 x_pixel_sigmas_squared,
2331 y_pixel_sigmas_squared;
2333 PixelChannel channel = GetPixelChannelChannel(image,i);
2334 PixelTrait traits = GetPixelChannelTraits(image,channel);
2335 PixelTrait reconstruct_traits = GetPixelChannelTraits(
2336 reconstruct_image,channel);
2337 if (((traits & UpdatePixelTrait) == 0) ||
2338 ((reconstruct_traits & UpdatePixelTrait) == 0))
2340 x_pixel_mu_squared=x_pixel_mu[i]*x_pixel_mu[i];
2341 y_pixel_mu_squared=y_pixel_mu[i]*y_pixel_mu[i];
2342 xy_mu=x_pixel_mu[i]*y_pixel_mu[i];
2343 xy_sigmas=xy_sigma[i]-xy_mu;
2344 x_pixel_sigmas_squared=x_pixel_sigma_squared[i]-x_pixel_mu_squared;
2345 y_pixel_sigmas_squared=y_pixel_sigma_squared[i]-y_pixel_mu_squared;
2346 ssim=((2.0*xy_mu+c1)*(2.0*xy_sigmas+c2))*
2347 MagickSafeReciprocal((x_pixel_mu_squared+y_pixel_mu_squared+c1)*
2348 (x_pixel_sigmas_squared+y_pixel_sigmas_squared+c2));
2349 channel_similarity[i]+=ssim;
2350 channel_similarity[CompositePixelChannel]+=ssim;
2352 p+=(ptrdiff_t) GetPixelChannels(image);
2353 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2356#if defined(MAGICKCORE_OPENMP_SUPPORT)
2357 #pragma omp critical (MagickCore_GetSSIMSimularity)
2364 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2366 PixelChannel channel = GetPixelChannelChannel(image,j);
2367 PixelTrait traits = GetPixelChannelTraits(image,channel);
2368 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2370 if (((traits & UpdatePixelTrait) == 0) ||
2371 ((reconstruct_traits & UpdatePixelTrait) == 0))
2373 similarity[j]+=channel_similarity[j];
2375 similarity[CompositePixelChannel]+=
2376 channel_similarity[CompositePixelChannel];
2379 image_view=DestroyCacheView(image_view);
2380 reconstruct_view=DestroyCacheView(reconstruct_view);
2381 area=MagickSafeReciprocal(area);
2382 for (l=0; l < (ssize_t) GetPixelChannels(image); l++)
2384 PixelChannel channel = GetPixelChannelChannel(image,l);
2385 PixelTrait traits = GetPixelChannelTraits(image,channel);
2386 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2388 if (((traits & UpdatePixelTrait) == 0) ||
2389 ((reconstruct_traits & UpdatePixelTrait) == 0))
2391 similarity[l]*=area;
2393 similarity[CompositePixelChannel]*=area;
2394 similarity[CompositePixelChannel]/=(double) GetImageChannels(image);
2395 kernel_info=DestroyKernelInfo(kernel_info);
2399static MagickBooleanType GetDSSIMSimilarity(
const Image *image,
2400 const Image *reconstruct_image,
double *similarity,ExceptionInfo *exception)
2403 status = MagickTrue;
2411 status=GetSSIMSimularity(image,reconstruct_image,similarity,exception);
2412 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2414 PixelChannel channel = GetPixelChannelChannel(image,i);
2415 PixelTrait traits = GetPixelChannelTraits(image,channel);
2416 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2418 if (((traits & UpdatePixelTrait) == 0) ||
2419 ((reconstruct_traits & UpdatePixelTrait) == 0))
2421 similarity[i]=(1.0-similarity[i])/2.0;
2423 similarity[CompositePixelChannel]=(1.0-similarity[CompositePixelChannel])/2.0;
2427MagickExport MagickBooleanType GetImageDistortion(Image *image,
2428 const Image *reconstruct_image,
const MetricType metric,
double *distortion,
2429 ExceptionInfo *exception)
2431#define CompareMetricNotSupportedException "metric not supported"
2434 *channel_similarity;
2437 status = MagickTrue;
2442 assert(image != (Image *) NULL);
2443 assert(image->signature == MagickCoreSignature);
2444 assert(reconstruct_image != (
const Image *) NULL);
2445 assert(reconstruct_image->signature == MagickCoreSignature);
2446 assert(distortion != (
double *) NULL);
2447 if (IsEventLogging() != MagickFalse)
2448 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2453 length=MaxPixelChannels+1UL;
2454 channel_similarity=(
double *) AcquireQuantumMemory(length,
2455 sizeof(*channel_similarity));
2456 if (channel_similarity == (
double *) NULL)
2457 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2458 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2461 case AbsoluteErrorMetric:
2463 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2467 case DotProductCorrelationErrorMetric:
2469 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2473 case FuzzErrorMetric:
2475 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2479 case MeanAbsoluteErrorMetric:
2481 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2485 case MeanErrorPerPixelErrorMetric:
2487 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2491 case MeanSquaredErrorMetric:
2493 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2497 case NormalizedCrossCorrelationErrorMetric:
2499 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2503 case PeakAbsoluteErrorMetric:
2505 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2509 case PeakSignalToNoiseRatioErrorMetric:
2511 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2515 case PerceptualHashErrorMetric:
2517 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2521 case PhaseCorrelationErrorMetric:
2523 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2527 case PixelDifferenceCountErrorMetric:
2529 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2533 case RootMeanSquaredErrorMetric:
2534 case UndefinedErrorMetric:
2537 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2541 case StructuralDissimilarityErrorMetric:
2543 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2547 case StructuralSimilarityErrorMetric:
2549 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2554 *distortion=channel_similarity[CompositePixelChannel];
2557 case DotProductCorrelationErrorMetric:
2558 case NormalizedCrossCorrelationErrorMetric:
2559 case PhaseCorrelationErrorMetric:
2560 case StructuralSimilarityErrorMetric:
2562 *distortion=(1.0-(*distortion))/2.0;
2567 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2568 if (fabs(*distortion) < MagickEpsilon)
2570 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2606MagickExport
double *GetImageDistortions(Image *image,
2607 const Image *reconstruct_image,
const MetricType metric,
2608 ExceptionInfo *exception)
2612 *channel_similarity;
2615 status = MagickTrue;
2623 assert(image != (Image *) NULL);
2624 assert(image->signature == MagickCoreSignature);
2625 assert(reconstruct_image != (
const Image *) NULL);
2626 assert(reconstruct_image->signature == MagickCoreSignature);
2627 if (IsEventLogging() != MagickFalse)
2628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2632 length=MaxPixelChannels+1UL;
2633 channel_similarity=(
double *) AcquireQuantumMemory(length,
2634 sizeof(*channel_similarity));
2635 if (channel_similarity == (
double *) NULL)
2636 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2637 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
2640 case AbsoluteErrorMetric:
2642 status=GetAESimilarity(image,reconstruct_image,channel_similarity,
2646 case DotProductCorrelationErrorMetric:
2648 status=GetDPCSimilarity(image,reconstruct_image,channel_similarity,
2652 case FuzzErrorMetric:
2654 status=GetFUZZSimilarity(image,reconstruct_image,channel_similarity,
2658 case MeanAbsoluteErrorMetric:
2660 status=GetMAESimilarity(image,reconstruct_image,channel_similarity,
2664 case MeanErrorPerPixelErrorMetric:
2666 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2670 case MeanSquaredErrorMetric:
2672 status=GetMSESimilarity(image,reconstruct_image,channel_similarity,
2676 case NormalizedCrossCorrelationErrorMetric:
2678 status=GetNCCSimilarity(image,reconstruct_image,channel_similarity,
2682 case PeakAbsoluteErrorMetric:
2684 status=GetPASimilarity(image,reconstruct_image,channel_similarity,
2688 case PeakSignalToNoiseRatioErrorMetric:
2690 status=GetPSNRSimilarity(image,reconstruct_image,channel_similarity,
2694 case PerceptualHashErrorMetric:
2696 status=GetPHASHSimilarity(image,reconstruct_image,channel_similarity,
2700 case PhaseCorrelationErrorMetric:
2702 status=GetPHASESimilarity(image,reconstruct_image,channel_similarity,
2706 case PixelDifferenceCountErrorMetric:
2708 status=GetPDCSimilarity(image,reconstruct_image,channel_similarity,
2712 case RootMeanSquaredErrorMetric:
2713 case UndefinedErrorMetric:
2716 status=GetRMSESimilarity(image,reconstruct_image,channel_similarity,
2720 case StructuralDissimilarityErrorMetric:
2722 status=GetDSSIMSimilarity(image,reconstruct_image,channel_similarity,
2726 case StructuralSimilarityErrorMetric:
2728 status=GetSSIMSimularity(image,reconstruct_image,channel_similarity,
2733 if (status == MagickFalse)
2735 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
2736 return((
double *) NULL);
2738 distortion=channel_similarity;
2741 case DotProductCorrelationErrorMetric:
2742 case NormalizedCrossCorrelationErrorMetric:
2743 case PhaseCorrelationErrorMetric:
2744 case StructuralSimilarityErrorMetric:
2746 for (i=0; i <= MaxPixelChannels; i++)
2747 distortion[i]=(1.0-distortion[i])/2.0;
2752 for (i=0; i <= MaxPixelChannels; i++)
2753 if (fabs(distortion[i]) < MagickEpsilon)
2755 (void) FormatImageProperty(image,
"distortion",
"%.*g",GetMagickPrecision(),
2756 distortion[CompositePixelChannel]);
2788MagickExport MagickBooleanType IsImagesEqual(
const Image *image,
2789 const Image *reconstruct_image,ExceptionInfo *exception)
2802 assert(image != (Image *) NULL);
2803 assert(image->signature == MagickCoreSignature);
2804 assert(reconstruct_image != (
const Image *) NULL);
2805 assert(reconstruct_image->signature == MagickCoreSignature);
2806 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
2807 image_view=AcquireVirtualCacheView(image,exception);
2808 reconstruct_view=AcquireVirtualCacheView(reconstruct_image,exception);
2809 for (y=0; y < (ssize_t) rows; y++)
2818 p=GetCacheViewVirtualPixels(image_view,0,y,columns,1,exception);
2819 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,columns,1,exception);
2820 if ((p == (
const Quantum *) NULL) || (q == (
const Quantum *) NULL))
2822 for (x=0; x < (ssize_t) columns; x++)
2827 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2832 PixelChannel channel = GetPixelChannelChannel(image,i);
2833 PixelTrait traits = GetPixelChannelTraits(image,channel);
2834 PixelTrait reconstruct_traits = GetPixelChannelTraits(reconstruct_image,
2836 if (((traits & UpdatePixelTrait) == 0) ||
2837 ((reconstruct_traits & UpdatePixelTrait) == 0))
2839 distance=fabs((
double) p[i]-(
double) GetPixelChannel(reconstruct_image,
2841 if (distance >= MagickEpsilon)
2844 if (i < (ssize_t) GetPixelChannels(image))
2846 p+=(ptrdiff_t) GetPixelChannels(image);
2847 q+=(ptrdiff_t) GetPixelChannels(reconstruct_image);
2849 if (x < (ssize_t) columns)
2852 reconstruct_view=DestroyCacheView(reconstruct_view);
2853 image_view=DestroyCacheView(image_view);
2854 return(y < (ssize_t) rows ? MagickFalse : MagickTrue);
2906MagickExport MagickBooleanType SetImageColorMetric(Image *image,
2907 const Image *reconstruct_image,ExceptionInfo *exception)
2910 channel_similarity[MaxPixelChannels+1] = { 0.0 };
2915 status=GetMEPPSimilarity(image,reconstruct_image,channel_similarity,
2917 if (status == MagickFalse)
2918 return(MagickFalse);
2919 status=fabs(image->error.mean_error_per_pixel) < MagickEpsilon ?
2920 MagickTrue : MagickFalse;
2967#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
2968static Image *SIMCrossCorrelationImage(
const Image *alpha_image,
2969 const Image *beta_image,ExceptionInfo *exception)
2972 *alpha_fft = (Image *) NULL,
2973 *beta_fft = (Image *) NULL,
2974 *complex_conjugate = (Image *) NULL,
2975 *complex_multiplication = (Image *) NULL,
2976 *cross_correlation = (Image *) NULL,
2977 *temp_image = (Image *) NULL;
2982 temp_image=CloneImage(beta_image,0,0,MagickTrue,exception);
2983 if (temp_image == (Image *) NULL)
2984 return((Image *) NULL);
2985 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
2986 beta_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
2987 temp_image=DestroyImageList(temp_image);
2988 if (beta_fft == (Image *) NULL)
2989 return((Image *) NULL);
2993 complex_conjugate=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
2994 beta_fft=DestroyImageList(beta_fft);
2995 if (complex_conjugate == (Image *) NULL)
2996 return((Image *) NULL);
3000 temp_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3001 if (temp_image == (Image *) NULL)
3003 complex_conjugate=DestroyImageList(complex_conjugate);
3004 return((Image *) NULL);
3006 (void) SetImageArtifact(temp_image,
"fourier:normalize",
"inverse");
3007 alpha_fft=ForwardFourierTransformImage(temp_image,MagickFalse,exception);
3008 temp_image=DestroyImageList(temp_image);
3009 if (alpha_fft == (Image *) NULL)
3011 complex_conjugate=DestroyImageList(complex_conjugate);
3012 return((Image *) NULL);
3017 DisableCompositeClampUnlessSpecified(complex_conjugate);
3018 DisableCompositeClampUnlessSpecified(complex_conjugate->next);
3019 AppendImageToList(&complex_conjugate,alpha_fft);
3020 complex_multiplication=ComplexImages(complex_conjugate,
3021 MultiplyComplexOperator,exception);
3022 complex_conjugate=DestroyImageList(complex_conjugate);
3023 if (complex_multiplication == (Image *) NULL)
3024 return((Image *) NULL);
3028 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3029 complex_multiplication->next,MagickFalse,exception);
3030 complex_multiplication=DestroyImageList(complex_multiplication);
3031 return(cross_correlation);
3034static Image *SIMDerivativeImage(
const Image *image,
const char *kernel,
3035 ExceptionInfo *exception)
3043 kernel_info=AcquireKernelInfo(kernel,exception);
3044 if (kernel_info == (KernelInfo *) NULL)
3045 return((Image *) NULL);
3046 derivative_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
3048 kernel_info=DestroyKernelInfo(kernel_info);
3049 return(derivative_image);
3052static Image *SIMDivideImage(
const Image *numerator_image,
3053 const Image *denominator_image,ExceptionInfo *exception)
3063 status = MagickTrue;
3071 divide_image=CloneImage(numerator_image,0,0,MagickTrue,exception);
3072 if (divide_image == (Image *) NULL)
3073 return(divide_image);
3074 numerator_view=AcquireAuthenticCacheView(divide_image,exception);
3075 denominator_view=AcquireVirtualCacheView(denominator_image,exception);
3076#if defined(MAGICKCORE_OPENMP_SUPPORT)
3077 #pragma omp parallel for schedule(static) shared(status) \
3078 magick_number_threads(denominator_image,divide_image,divide_image->rows,1)
3080 for (y=0; y < (ssize_t) divide_image->rows; y++)
3091 if (status == MagickFalse)
3093 p=GetCacheViewVirtualPixels(denominator_view,0,y,
3094 denominator_image->columns,1,exception);
3095 q=GetCacheViewAuthenticPixels(numerator_view,0,y,divide_image->columns,1,
3097 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3102 for (x=0; x < (ssize_t) divide_image->columns; x++)
3107 for (i=0; i < (ssize_t) GetPixelChannels(divide_image); i++)
3109 PixelChannel channel = GetPixelChannelChannel(divide_image,i);
3110 PixelTrait traits = GetPixelChannelTraits(divide_image,channel);
3111 PixelTrait denominator_traits = GetPixelChannelTraits(denominator_image,
3113 if (((traits & UpdatePixelTrait) == 0) ||
3114 ((denominator_traits & UpdatePixelTrait) == 0))
3116 q[i]=(Quantum) ((
double) q[i]*MagickSafeReciprocal(QuantumScale*
3117 (
double) GetPixelChannel(denominator_image,channel,p)));
3119 p+=(ptrdiff_t) GetPixelChannels(denominator_image);
3120 q+=(ptrdiff_t) GetPixelChannels(divide_image);
3122 if (SyncCacheViewAuthenticPixels(numerator_view,exception) == MagickFalse)
3125 denominator_view=DestroyCacheView(denominator_view);
3126 numerator_view=DestroyCacheView(numerator_view);
3127 if (status == MagickFalse)
3128 divide_image=DestroyImage(divide_image);
3129 return(divide_image);
3132static Image *SIMDivideByMagnitude(Image *image,Image *magnitude_image,
3133 const Image *source_image,ExceptionInfo *exception)
3142 divide_image=SIMDivideImage(image,magnitude_image,exception);
3143 if (divide_image == (Image *) NULL)
3144 return((Image *) NULL);
3145 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
3146 ÷_image->background_color);
3147 SetGeometry(source_image,&geometry);
3148 geometry.width=MagickMax(source_image->columns,divide_image->columns);
3149 geometry.height=MagickMax(source_image->rows,divide_image->rows);
3150 result_image=ExtentImage(divide_image,&geometry,exception);
3151 divide_image=DestroyImage(divide_image);
3152 return(result_image);
3155static MagickBooleanType SIMFilterImageNaNs(Image *image,
3156 ExceptionInfo *exception)
3162 status = MagickTrue;
3170 image_view=AcquireAuthenticCacheView(image,exception);
3171#if defined(MAGICKCORE_OPENMP_SUPPORT)
3172 #pragma omp parallel for schedule(static) shared(status) \
3173 magick_number_threads(image,image,image->rows,1)
3175 for (y=0; y < (ssize_t) image->rows; y++)
3183 if (status == MagickFalse)
3185 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3186 if (q == (Quantum *) NULL)
3191 for (x=0; x < (ssize_t) image->columns; x++)
3196 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3198 PixelChannel channel = GetPixelChannelChannel(image,i);
3199 PixelTrait traits = GetPixelChannelTraits(image,channel);
3200 if ((traits & UpdatePixelTrait) == 0)
3202 if (IsNaN((
double) q[i]) != 0)
3205 q+=(ptrdiff_t) GetPixelChannels(image);
3207 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3210 image_view=DestroyCacheView(image_view);
3214static Image *SIMSquareImage(
const Image *image,ExceptionInfo *exception)
3223 status = MagickTrue;
3231 square_image=CloneImage(image,0,0,MagickTrue,exception);
3232 if (square_image == (Image *) NULL)
3233 return(square_image);
3234 image_view=AcquireAuthenticCacheView(square_image,exception);
3235#if defined(MAGICKCORE_OPENMP_SUPPORT)
3236 #pragma omp parallel for schedule(static) shared(status) \
3237 magick_number_threads(square_image,square_image,square_image->rows,1)
3239 for (y=0; y < (ssize_t) square_image->rows; y++)
3247 if (status == MagickFalse)
3249 q=GetCacheViewAuthenticPixels(image_view,0,y,square_image->columns,1,
3251 if (q == (Quantum *) NULL)
3256 for (x=0; x < (ssize_t) square_image->columns; x++)
3261 for (i=0; i < (ssize_t) GetPixelChannels(square_image); i++)
3263 PixelChannel channel = GetPixelChannelChannel(square_image,i);
3264 PixelTrait traits = GetPixelChannelTraits(square_image,channel);
3265 if ((traits & UpdatePixelTrait) == 0)
3267 q[i]=(Quantum) (QuantumScale*q[i]*q[i]);
3269 q+=(ptrdiff_t) GetPixelChannels(square_image);
3271 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3274 image_view=DestroyCacheView(image_view);
3275 if (status == MagickFalse)
3276 square_image=DestroyImage(square_image);
3277 return(square_image);
3280static Image *SIMMagnitudeImage(Image *alpha_image,Image *beta_image,
3281 ExceptionInfo *exception)
3289 status = MagickTrue;
3291 (void) SetImageArtifact(alpha_image,
"compose:clamp",
"False");
3292 xsq_image=SIMSquareImage(alpha_image,exception);
3293 if (xsq_image == (Image *) NULL)
3294 return((Image *) NULL);
3295 (void) SetImageArtifact(beta_image,
"compose:clamp",
"False");
3296 ysq_image=SIMSquareImage(beta_image,exception);
3297 if (ysq_image == (Image *) NULL)
3299 xsq_image=DestroyImage(xsq_image);
3300 return((Image *) NULL);
3302 status=CompositeImage(xsq_image,ysq_image,PlusCompositeOp,MagickTrue,0,0,
3304 magnitude_image=xsq_image;
3305 ysq_image=DestroyImage(ysq_image);
3306 if (status == MagickFalse)
3308 magnitude_image=DestroyImage(magnitude_image);
3309 return((Image *) NULL);
3311 status=EvaluateImage(magnitude_image,PowEvaluateOperator,0.5,exception);
3312 if (status == MagickFalse)
3314 magnitude_image=DestroyImage(magnitude_image);
3315 return (Image *) NULL;
3317 return(magnitude_image);
3320static MagickBooleanType SIMMaximaImage(
const Image *image,
double *maxima,
3321 RectangleInfo *offset,ExceptionInfo *exception)
3340 status = MagickTrue;
3343 maxima_info = { -MagickMaximumValue, 0, 0 };
3351 image_view=AcquireVirtualCacheView(image,exception);
3352 q=GetCacheViewVirtualPixels(image_view,maxima_info.x,maxima_info.y,1,1,
3354 if (q != (
const Quantum *) NULL)
3355 maxima_info.maxima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3356#if defined(MAGICKCORE_OPENMP_SUPPORT)
3357 #pragma omp parallel for schedule(static) shared(maxima_info,status) \
3358 magick_number_threads(image,image,image->rows,1)
3360 for (y=0; y < (ssize_t) image->rows; y++)
3366 channel_maxima = { -MagickMaximumValue, 0, 0 };
3371 if (status == MagickFalse)
3373 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3374 if (p == (
const Quantum *) NULL)
3379 channel_maxima=maxima_info;
3380 for (x=0; x < (ssize_t) image->columns; x++)
3385 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3390 PixelChannel channel = GetPixelChannelChannel(image,i);
3391 PixelTrait traits = GetPixelChannelTraits(image,channel);
3392 if ((traits & UpdatePixelTrait) == 0)
3394 pixel=(double) p[i];
3395 if (IsNaN(pixel) != 0)
3397 if (pixel > channel_maxima.maxima)
3399 channel_maxima.maxima=(double) p[i];
3404 p+=(ptrdiff_t) GetPixelChannels(image);
3406#if defined(MAGICKCORE_OPENMP_SUPPORT)
3407 #pragma omp critical (MagickCore_SIMMaximaImage)
3409 if (channel_maxima.maxima > maxima_info.maxima)
3410 maxima_info=channel_maxima;
3412 image_view=DestroyCacheView(image_view);
3413 *maxima=maxima_info.maxima;
3414 offset->x=maxima_info.x;
3415 offset->y=maxima_info.y;
3419static MagickBooleanType SIMMinimaImage(
const Image *image,
double *minima,
3420 RectangleInfo *offset,ExceptionInfo *exception)
3439 status = MagickTrue;
3442 minima_info = { MagickMaximumValue, 0, 0 };
3450 image_view=AcquireVirtualCacheView(image,exception);
3451 q=GetCacheViewVirtualPixels(image_view,minima_info.x,minima_info.y,1,1,
3453 if (q != (
const Quantum *) NULL)
3454 minima_info.minima=IsNaN((
double) q[0]) != 0 ? 0.0 : (double) q[0];
3455#if defined(MAGICKCORE_OPENMP_SUPPORT)
3456 #pragma omp parallel for schedule(static) shared(minima_info,status) \
3457 magick_number_threads(image,image,image->rows,1)
3459 for (y=0; y < (ssize_t) image->rows; y++)
3465 channel_minima = { MagickMaximumValue, 0, 0 };
3470 if (status == MagickFalse)
3472 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3473 if (p == (
const Quantum *) NULL)
3478 channel_minima=minima_info;
3479 for (x=0; x < (ssize_t) image->columns; x++)
3484 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3489 PixelChannel channel = GetPixelChannelChannel(image,i);
3490 PixelTrait traits = GetPixelChannelTraits(image,channel);
3491 if ((traits & UpdatePixelTrait) == 0)
3493 pixel=(double) p[i];
3494 if (IsNaN(pixel) != 0)
3496 if (pixel < channel_minima.minima)
3498 channel_minima.minima=pixel;
3503 p+=(ptrdiff_t) GetPixelChannels(image);
3505#if defined(MAGICKCORE_OPENMP_SUPPORT)
3506 #pragma omp critical (MagickCore_SIMMinimaImage)
3508 if (channel_minima.minima < minima_info.minima)
3509 minima_info=channel_minima;
3511 image_view=DestroyCacheView(image_view);
3512 *minima=minima_info.minima;
3513 offset->x=minima_info.x;
3514 offset->y=minima_info.y;
3518static MagickBooleanType SIMMultiplyImage(Image *image,
const double factor,
3519 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3525 status = MagickTrue;
3533 image_view=AcquireAuthenticCacheView(image,exception);
3534#if defined(MAGICKCORE_OPENMP_SUPPORT)
3535 #pragma omp parallel for schedule(static) shared(status) \
3536 magick_number_threads(image,image,image->rows,1)
3538 for (y=0; y < (ssize_t) image->rows; y++)
3546 if (status == MagickFalse)
3548 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3549 if (q == (Quantum *) NULL)
3554 for (x=0; x < (ssize_t) image->columns; x++)
3559 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3561 PixelChannel channel = GetPixelChannelChannel(image,i);
3562 PixelTrait traits = GetPixelChannelTraits(image,channel);
3563 if ((traits & UpdatePixelTrait) == 0)
3565 if (channel_statistics != (
const ChannelStatistics *) NULL)
3566 q[i]=(Quantum) (factor*q[i]*QuantumScale*
3567 channel_statistics[channel].standard_deviation);
3569 q[i]=(Quantum) (factor*q[i]);
3571 q+=(ptrdiff_t) GetPixelChannels(image);
3573 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3576 image_view=DestroyCacheView(image_view);
3580static Image *SIMPhaseCorrelationImage(
const Image *alpha_image,
3581 const Image *beta_image,
const Image *magnitude_image,ExceptionInfo *exception)
3584 *alpha_fft = (Image *) NULL,
3585 *beta_fft = (Image *) NULL,
3586 *complex_multiplication = (Image *) NULL,
3587 *cross_correlation = (Image *) NULL;
3592 beta_fft=CloneImage(beta_image,0,0,MagickTrue,exception);
3593 if (beta_fft == NULL)
3594 return((Image *) NULL);
3595 (void) SetImageArtifact(beta_fft,
"fourier:normalize",
"inverse");
3596 beta_fft=ForwardFourierTransformImage(beta_fft,MagickFalse,exception);
3597 if (beta_fft == NULL)
3598 return((Image *) NULL);
3602 alpha_fft=CloneImage(alpha_image,0,0,MagickTrue,exception);
3603 if (alpha_fft == (Image *) NULL)
3605 beta_fft=DestroyImageList(beta_fft);
3606 return((Image *) NULL);
3608 (void) SetImageArtifact(alpha_fft,
"fourier:normalize",
"inverse");
3609 alpha_fft=ForwardFourierTransformImage(alpha_fft,MagickFalse,exception);
3610 if (alpha_fft == (Image *) NULL)
3612 beta_fft=DestroyImageList(beta_fft);
3613 return((Image *) NULL);
3618 beta_fft=ComplexImages(beta_fft,ConjugateComplexOperator,exception);
3619 if (beta_fft == (Image *) NULL)
3621 alpha_fft=DestroyImageList(alpha_fft);
3622 return((Image *) NULL);
3627 AppendImageToList(&beta_fft,alpha_fft);
3628 DisableCompositeClampUnlessSpecified(beta_fft);
3629 DisableCompositeClampUnlessSpecified(beta_fft->next);
3630 complex_multiplication=ComplexImages(beta_fft,MultiplyComplexOperator,
3632 beta_fft=DestroyImageList(beta_fft);
3633 if (complex_multiplication == (Image *) NULL)
3634 return((Image *) NULL);
3638 CompositeLayers(complex_multiplication,DivideSrcCompositeOp,(Image *)
3639 magnitude_image,0,0,exception);
3643 (void) SetImageArtifact(complex_multiplication,
"fourier:normalize",
"inverse");
3644 cross_correlation=InverseFourierTransformImage(complex_multiplication,
3645 complex_multiplication->next,MagickFalse,exception);
3646 complex_multiplication=DestroyImageList(complex_multiplication);
3647 return(cross_correlation);
3650static MagickBooleanType SIMSetImageMean(Image *image,
3651 const ChannelStatistics *channel_statistics,ExceptionInfo *exception)
3657 status = MagickTrue;
3665 image_view=AcquireAuthenticCacheView(image,exception);
3666#if defined(MAGICKCORE_OPENMP_SUPPORT)
3667 #pragma omp parallel for schedule(static) shared(status) \
3668 magick_number_threads(image,image,image->rows,1)
3670 for (y=0; y < (ssize_t) image->rows; y++)
3678 if (status == MagickFalse)
3680 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3681 if (q == (Quantum *) NULL)
3686 for (x=0; x < (ssize_t) image->columns; x++)
3691 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3693 PixelChannel channel = GetPixelChannelChannel(image,i);
3694 PixelTrait traits = GetPixelChannelTraits(image,channel);
3695 if ((traits & UpdatePixelTrait) == 0)
3697 q[i]=(Quantum) channel_statistics[channel].mean;
3699 q+=(ptrdiff_t) GetPixelChannels(image);
3701 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3704 image_view=DestroyCacheView(image_view);
3708static Image *SIMSubtractImageMean(
const Image *alpha_image,
3709 const Image *beta_image,
const ChannelStatistics *channel_statistics,
3710 ExceptionInfo *exception)
3720 status = MagickTrue;
3728 subtract_image=CloneImage(beta_image,alpha_image->columns,alpha_image->rows,
3729 MagickTrue,exception);
3730 if (subtract_image == (Image *) NULL)
3731 return(subtract_image);
3732 image_view=AcquireAuthenticCacheView(subtract_image,exception);
3733 beta_view=AcquireVirtualCacheView(beta_image,exception);
3734#if defined(MAGICKCORE_OPENMP_SUPPORT)
3735 #pragma omp parallel for schedule(static) shared(status) \
3736 magick_number_threads(beta_image,subtract_image,subtract_image->rows,1)
3738 for (y=0; y < (ssize_t) subtract_image->rows; y++)
3749 if (status == MagickFalse)
3751 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,exception);
3752 q=GetCacheViewAuthenticPixels(image_view,0,y,subtract_image->columns,1,
3754 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3759 for (x=0; x < (ssize_t) subtract_image->columns; x++)
3764 for (i=0; i < (ssize_t) GetPixelChannels(subtract_image); i++)
3766 PixelChannel channel = GetPixelChannelChannel(subtract_image,i);
3767 PixelTrait traits = GetPixelChannelTraits(subtract_image,channel);
3768 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3769 if (((traits & UpdatePixelTrait) == 0) ||
3770 ((beta_traits & UpdatePixelTrait) == 0))
3772 if ((x >= (ssize_t) beta_image->columns) ||
3773 (y >= (ssize_t) beta_image->rows))
3776 q[i]=(Quantum) ((
double) GetPixelChannel(beta_image,channel,p)-
3777 channel_statistics[channel].mean);
3779 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3780 q+=(ptrdiff_t) GetPixelChannels(subtract_image);
3782 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3785 beta_view=DestroyCacheView(beta_view);
3786 image_view=DestroyCacheView(image_view);
3787 if (status == MagickFalse)
3788 subtract_image=DestroyImage(subtract_image);
3789 return(subtract_image);
3792static Image *SIMUnityImage(
const Image *alpha_image,
const Image *beta_image,
3793 ExceptionInfo *exception)
3802 status = MagickTrue;
3810 unity_image=CloneImage(alpha_image,alpha_image->columns,alpha_image->rows,
3811 MagickTrue,exception);
3812 if (unity_image == (Image *) NULL)
3813 return(unity_image);
3814 if (SetImageStorageClass(unity_image,DirectClass,exception) == MagickFalse)
3815 return(DestroyImage(unity_image));
3816 image_view=AcquireAuthenticCacheView(unity_image,exception);
3817#if defined(MAGICKCORE_OPENMP_SUPPORT)
3818 #pragma omp parallel for schedule(static) shared(status) \
3819 magick_number_threads(unity_image,unity_image,unity_image->rows,1)
3821 for (y=0; y < (ssize_t) unity_image->rows; y++)
3829 if (status == MagickFalse)
3831 q=GetCacheViewAuthenticPixels(image_view,0,y,unity_image->columns,1,
3833 if (q == (Quantum *) NULL)
3838 for (x=0; x < (ssize_t) unity_image->columns; x++)
3843 for (i=0; i < (ssize_t) GetPixelChannels(unity_image); i++)
3845 PixelChannel channel = GetPixelChannelChannel(unity_image,i);
3846 PixelTrait traits = GetPixelChannelTraits(unity_image,channel);
3847 if ((traits & UpdatePixelTrait) == 0)
3849 if ((x >= (ssize_t) beta_image->columns) ||
3850 (y >= (ssize_t) beta_image->rows))
3855 q+=(ptrdiff_t) GetPixelChannels(unity_image);
3857 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3860 image_view=DestroyCacheView(image_view);
3861 if (status == MagickFalse)
3862 unity_image=DestroyImage(unity_image);
3863 return(unity_image);
3866static Image *SIMVarianceImage(Image *alpha_image,
const Image *beta_image,
3867 ExceptionInfo *exception)
3877 status = MagickTrue;
3885 variance_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
3886 if (variance_image == (Image *) NULL)
3887 return(variance_image);
3888 image_view=AcquireAuthenticCacheView(variance_image,exception);
3889 beta_view=AcquireVirtualCacheView(beta_image,exception);
3890#if defined(MAGICKCORE_OPENMP_SUPPORT)
3891 #pragma omp parallel for schedule(static) shared(status) \
3892 magick_number_threads(beta_image,variance_image,variance_image->rows,1)
3894 for (y=0; y < (ssize_t) variance_image->rows; y++)
3905 if (status == MagickFalse)
3907 p=GetCacheViewVirtualPixels(beta_view,0,y,beta_image->columns,1,
3909 q=GetCacheViewAuthenticPixels(image_view,0,y,variance_image->columns,1,
3911 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3916 for (x=0; x < (ssize_t) variance_image->columns; x++)
3921 for (i=0; i < (ssize_t) GetPixelChannels(variance_image); i++)
3926 PixelChannel channel = GetPixelChannelChannel(variance_image,i);
3927 PixelTrait traits = GetPixelChannelTraits(variance_image,channel);
3928 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
3929 if (((traits & UpdatePixelTrait) == 0) ||
3930 ((beta_traits & UpdatePixelTrait) == 0))
3932 error=(double) q[i]-(
double) GetPixelChannel(beta_image,channel,p);
3933 q[i]=(Quantum) ((
double) ClampToQuantum((
double) QuantumRange*
3934 (sqrt(fabs(QuantumScale*error))/sqrt((
double) QuantumRange))));
3936 p+=(ptrdiff_t) GetPixelChannels(beta_image);
3937 q+=(ptrdiff_t) GetPixelChannels(variance_image);
3939 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3942 beta_view=DestroyCacheView(beta_view);
3943 image_view=DestroyCacheView(image_view);
3944 if (status == MagickFalse)
3945 variance_image=DestroyImage(variance_image);
3946 return(variance_image);
3949static Image *DPCSimilarityImage(
const Image *image,
const Image *reconstruct,
3950 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
3952#define ThrowDPCSimilarityException() \
3954 if (dot_product_image != (Image *) NULL) \
3955 dot_product_image=DestroyImage(dot_product_image); \
3956 if (magnitude_image != (Image *) NULL) \
3957 magnitude_image=DestroyImage(magnitude_image); \
3958 if (reconstruct_image != (Image *) NULL) \
3959 reconstruct_image=DestroyImage(reconstruct_image); \
3960 if (rx_image != (Image *) NULL) \
3961 rx_image=DestroyImage(rx_image); \
3962 if (ry_image != (Image *) NULL) \
3963 ry_image=DestroyImage(ry_image); \
3964 if (target_image != (Image *) NULL) \
3965 target_image=DestroyImage(target_image); \
3966 if (threshold_image != (Image *) NULL) \
3967 threshold_image=DestroyImage(threshold_image); \
3968 if (trx_image != (Image *) NULL) \
3969 trx_image=DestroyImage(trx_image); \
3970 if (try_image != (Image *) NULL) \
3971 try_image=DestroyImage(try_image); \
3972 if (tx_image != (Image *) NULL) \
3973 tx_image=DestroyImage(tx_image); \
3974 if (ty_image != (Image *) NULL) \
3975 ty_image=DestroyImage(ty_image); \
3976 return((Image *) NULL); \
3983 standard_deviation = 0.0;
3986 *dot_product_image = (Image *) NULL,
3987 *magnitude_image = (Image *) NULL,
3988 *reconstruct_image = (Image *) NULL,
3989 *rx_image = (Image *) NULL,
3990 *ry_image = (Image *) NULL,
3991 *trx_image = (Image *) NULL,
3992 *target_image = (Image *) NULL,
3993 *threshold_image = (Image *) NULL,
3994 *try_image = (Image *) NULL,
3995 *tx_image = (Image *) NULL,
3996 *ty_image = (Image *) NULL;
3999 status = MagickTrue;
4007 target_image=CloneImage(image,0,0,MagickTrue,exception);
4008 if (target_image == (Image *) NULL)
4009 return((Image *) NULL);
4013 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4014 if (reconstruct_image == (Image *) NULL)
4015 ThrowDPCSimilarityException();
4019 (void) SetImageVirtualPixelMethod(reconstruct_image,EdgeVirtualPixelMethod,
4021 rx_image=SIMDerivativeImage(reconstruct_image,
"Sobel",exception);
4022 if (rx_image == (Image *) NULL)
4023 ThrowDPCSimilarityException();
4024 ry_image=SIMDerivativeImage(reconstruct_image,
"Sobel:90",exception);
4025 reconstruct_image=DestroyImage(reconstruct_image);
4026 if (ry_image == (Image *) NULL)
4027 ThrowDPCSimilarityException();
4031 magnitude_image=SIMMagnitudeImage(rx_image,ry_image,exception);
4032 if (magnitude_image == (Image *) NULL)
4033 ThrowDPCSimilarityException();
4037 threshold_image=CloneImage(magnitude_image,0,0,MagickTrue,exception);
4038 if (threshold_image == (Image *) NULL)
4039 ThrowDPCSimilarityException();
4040 status=BilevelImage(threshold_image,0.0,exception);
4041 if (status == MagickFalse)
4042 ThrowDPCSimilarityException();
4043 status=GetImageMean(threshold_image,&mean,&standard_deviation,exception);
4044 threshold_image=DestroyImage(threshold_image);
4045 if (status == MagickFalse)
4046 ThrowDPCSimilarityException();
4047 edge_factor=MagickSafeReciprocal(QuantumScale*mean*reconstruct->columns*
4048 reconstruct->rows)+QuantumScale;
4052 trx_image=SIMDivideByMagnitude(rx_image,magnitude_image,image,exception);
4053 rx_image=DestroyImage(rx_image);
4054 if (trx_image == (Image *) NULL)
4055 ThrowDPCSimilarityException();
4057 try_image=SIMDivideByMagnitude(ry_image,magnitude_image,image,exception);
4058 magnitude_image=DestroyImage(magnitude_image);
4059 ry_image=DestroyImage(ry_image);
4060 if (try_image == (Image *) NULL)
4061 ThrowDPCSimilarityException();
4066 (void) SetImageVirtualPixelMethod(target_image,EdgeVirtualPixelMethod,
4068 tx_image=SIMDerivativeImage(target_image,
"Sobel",exception);
4069 if (tx_image == (Image *) NULL)
4070 ThrowDPCSimilarityException();
4071 ty_image=SIMDerivativeImage(target_image,
"Sobel:90",exception);
4072 target_image=DestroyImage(target_image);
4073 if (ty_image == (Image *) NULL)
4074 ThrowDPCSimilarityException();
4078 magnitude_image=SIMMagnitudeImage(tx_image,ty_image,exception);
4079 if (magnitude_image == (Image *) NULL)
4080 ThrowDPCSimilarityException();
4084 trx_image=SIMDivideByMagnitude(tx_image,magnitude_image,image,exception);
4085 tx_image=DestroyImage(tx_image);
4086 if (trx_image == (Image *) NULL)
4087 ThrowDPCSimilarityException();
4089 try_image=SIMDivideByMagnitude(ty_image,magnitude_image,image,exception);
4090 ty_image=DestroyImage(ty_image);
4091 magnitude_image=DestroyImage(magnitude_image);
4092 if (try_image == (Image *) NULL)
4093 ThrowDPCSimilarityException();
4098 trx_image=SIMCrossCorrelationImage(tx_image,rx_image,exception);
4099 rx_image=DestroyImage(rx_image);
4100 tx_image=DestroyImage(tx_image);
4101 if (trx_image == (Image *) NULL)
4102 ThrowDPCSimilarityException();
4103 try_image=SIMCrossCorrelationImage(ty_image,ry_image,exception);
4104 ry_image=DestroyImage(ry_image);
4105 ty_image=DestroyImage(ty_image);
4106 if (try_image == (Image *) NULL)
4107 ThrowDPCSimilarityException();
4111 (void) SetImageArtifact(try_image,
"compose:clamp",
"false");
4112 status=CompositeImage(trx_image,try_image,PlusCompositeOp,MagickTrue,0,0,
4114 try_image=DestroyImage(try_image);
4115 if (status == MagickFalse)
4116 ThrowDPCSimilarityException();
4117 status=SIMMultiplyImage(trx_image,edge_factor,
4118 (
const ChannelStatistics *) NULL,exception);
4119 if (status == MagickFalse)
4120 ThrowDPCSimilarityException();
4124 SetGeometry(image,&geometry);
4125 geometry.width=image->columns;
4126 geometry.height=image->rows;
4127 (void) ResetImagePage(trx_image,
"0x0+0+0");
4128 dot_product_image=CropImage(trx_image,&geometry,exception);
4129 trx_image=DestroyImage(trx_image);
4130 if (dot_product_image == (Image *) NULL)
4131 ThrowDPCSimilarityException();
4132 (void) ResetImagePage(dot_product_image,
"0x0+0+0");
4136 status=GrayscaleImage(dot_product_image,AveragePixelIntensityMethod,
4138 if (status == MagickFalse)
4139 ThrowDPCSimilarityException();
4140 dot_product_image->depth=32;
4141 dot_product_image->colorspace=GRAYColorspace;
4142 dot_product_image->alpha_trait=UndefinedPixelTrait;
4143 status=SIMFilterImageNaNs(dot_product_image,exception);
4144 if (status == MagickFalse)
4145 ThrowDPCSimilarityException();
4146 status=SIMMaximaImage(dot_product_image,&maxima,offset,exception);
4147 if (status == MagickFalse)
4148 ThrowDPCSimilarityException();
4149 if ((QuantumScale*maxima) > 1.0)
4151 status=SIMMultiplyImage(dot_product_image,1.0/(QuantumScale*maxima),
4152 (
const ChannelStatistics *) NULL,exception);
4153 maxima=(double) QuantumRange;
4155 *similarity_metric=QuantumScale*maxima;
4156 return(dot_product_image);
4159static Image *MSESimilarityImage(
const Image *image,
const Image *reconstruct,
4160 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4162#define ThrowMSESimilarityException() \
4164 if (alpha_image != (Image *) NULL) \
4165 alpha_image=DestroyImage(alpha_image); \
4166 if (beta_image != (Image *) NULL) \
4167 beta_image=DestroyImage(beta_image); \
4168 if (channel_statistics != (ChannelStatistics *) NULL) \
4169 channel_statistics=(ChannelStatistics *) \
4170 RelinquishMagickMemory(channel_statistics); \
4171 if (mean_image != (Image *) NULL) \
4172 mean_image=DestroyImage(mean_image); \
4173 if (mse_image != (Image *) NULL) \
4174 mse_image=DestroyImage(mse_image); \
4175 if (reconstruct_image != (Image *) NULL) \
4176 reconstruct_image=DestroyImage(reconstruct_image); \
4177 if (sum_image != (Image *) NULL) \
4178 sum_image=DestroyImage(sum_image); \
4179 if (alpha_image != (Image *) NULL) \
4180 alpha_image=DestroyImage(alpha_image); \
4181 return((Image *) NULL); \
4185 *channel_statistics = (ChannelStatistics *) NULL;
4191 *alpha_image = (Image *) NULL,
4192 *beta_image = (Image *) NULL,
4193 *mean_image = (Image *) NULL,
4194 *mse_image = (Image *) NULL,
4195 *reconstruct_image = (Image *) NULL,
4196 *sum_image = (Image *) NULL,
4197 *target_image = (Image *) NULL;
4200 status = MagickTrue;
4208 target_image=SIMSquareImage(image,exception);
4209 if (target_image == (Image *) NULL)
4210 ThrowMSESimilarityException();
4211 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4212 if (reconstruct_image == (Image *) NULL)
4213 ThrowMSESimilarityException();
4217 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4219 target_image=DestroyImage(target_image);
4220 if (alpha_image == (Image *) NULL)
4221 ThrowMSESimilarityException();
4222 status=SIMMultiplyImage(alpha_image,1.0/reconstruct->columns/(
double)
4223 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4224 if (status == MagickFalse)
4225 ThrowMSESimilarityException();
4229 (void) CompositeImage(reconstruct_image,reconstruct,CopyCompositeOp,
4230 MagickTrue,0,0,exception);
4231 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4232 if (beta_image == (Image *) NULL)
4234 reconstruct_image=DestroyImage(reconstruct_image);
4235 ThrowMSESimilarityException();
4237 status=SIMMultiplyImage(beta_image,-2.0/reconstruct->columns/(
double)
4238 reconstruct->rows,(
const ChannelStatistics *) NULL,exception);
4239 reconstruct_image=DestroyImage(reconstruct_image);
4240 if (status == MagickFalse)
4241 ThrowMSESimilarityException();
4245 sum_image=SIMSquareImage(reconstruct,exception);
4246 if (sum_image == (Image *) NULL)
4247 ThrowMSESimilarityException();
4248 channel_statistics=GetImageStatistics(sum_image,exception);
4249 if (channel_statistics == (ChannelStatistics *) NULL)
4250 ThrowMSESimilarityException();
4251 status=SetImageStorageClass(sum_image,DirectClass,exception);
4252 if (status == MagickFalse)
4253 ThrowMSESimilarityException();
4254 status=SIMSetImageMean(sum_image,channel_statistics,exception);
4255 channel_statistics=(ChannelStatistics *)
4256 RelinquishMagickMemory(channel_statistics);
4257 if (status == MagickFalse)
4258 ThrowMSESimilarityException();
4262 AppendImageToList(&sum_image,alpha_image);
4263 AppendImageToList(&sum_image,beta_image);
4264 mean_image=EvaluateImages(sum_image,SumEvaluateOperator,exception);
4265 if (mean_image == (Image *) NULL)
4266 ThrowMSESimilarityException();
4267 sum_image=DestroyImage(sum_image);
4268 status=GrayscaleImage(mean_image,AveragePixelIntensityMethod,exception);
4269 if (status == MagickFalse)
4270 ThrowMSESimilarityException();
4274 SetGeometry(image,&geometry);
4275 geometry.width=image->columns;
4276 geometry.height=image->rows;
4277 (void) ResetImagePage(mean_image,
"0x0+0+0");
4278 mse_image=CropImage(mean_image,&geometry,exception);
4279 mean_image=DestroyImage(mean_image);
4280 if (mse_image == (Image *) NULL)
4281 ThrowMSESimilarityException();
4285 (void) ResetImagePage(mse_image,
"0x0+0+0");
4286 (void) ClampImage(mse_image,exception);
4287 mse_image->depth=32;
4288 mse_image->colorspace=GRAYColorspace;
4289 mse_image->alpha_trait=UndefinedPixelTrait;
4290 status=SIMMinimaImage(mse_image,&minima,offset,exception);
4291 if (status == MagickFalse)
4292 ThrowMSESimilarityException();
4293 status=NegateImage(mse_image,MagickFalse,exception);
4294 if (status == MagickFalse)
4295 ThrowMSESimilarityException();
4296 alpha_image=DestroyImage(alpha_image);
4297 beta_image=DestroyImage(beta_image);
4298 if ((QuantumScale*minima) < FLT_EPSILON)
4300 *similarity_metric=QuantumScale*minima;
4304static Image *NCCSimilarityImage(
const Image *image,
const Image *reconstruct,
4305 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4307#define ThrowNCCSimilarityException() \
4309 if (alpha_image != (Image *) NULL) \
4310 alpha_image=DestroyImage(alpha_image); \
4311 if (beta_image != (Image *) NULL) \
4312 beta_image=DestroyImage(beta_image); \
4313 if (channel_statistics != (ChannelStatistics *) NULL) \
4314 channel_statistics=(ChannelStatistics *) \
4315 RelinquishMagickMemory(channel_statistics); \
4316 if (correlation_image != (Image *) NULL) \
4317 correlation_image=DestroyImage(correlation_image); \
4318 if (divide_image != (Image *) NULL) \
4319 divide_image=DestroyImage(divide_image); \
4320 if (ncc_image != (Image *) NULL) \
4321 ncc_image=DestroyImage(ncc_image); \
4322 if (normalize_image != (Image *) NULL) \
4323 normalize_image=DestroyImage(normalize_image); \
4324 if (reconstruct_image != (Image *) NULL) \
4325 reconstruct_image=DestroyImage(reconstruct_image); \
4326 if (target_image != (Image *) NULL) \
4327 target_image=DestroyImage(target_image); \
4328 if (variance_image != (Image *) NULL) \
4329 variance_image=DestroyImage(variance_image); \
4330 return((Image *) NULL); \
4334 *channel_statistics = (ChannelStatistics *) NULL;
4340 *alpha_image = (Image *) NULL,
4341 *beta_image = (Image *) NULL,
4342 *correlation_image = (Image *) NULL,
4343 *divide_image = (Image *) NULL,
4344 *ncc_image = (Image *) NULL,
4345 *normalize_image = (Image *) NULL,
4346 *reconstruct_image = (Image *) NULL,
4347 *target_image = (Image *) NULL,
4348 *variance_image = (Image *) NULL;
4351 status = MagickTrue;
4359 target_image=SIMSquareImage(image,exception);
4360 if (target_image == (Image *) NULL)
4361 ThrowNCCSimilarityException();
4362 reconstruct_image=SIMUnityImage(image,reconstruct,exception);
4363 if (reconstruct_image == (Image *) NULL)
4364 ThrowNCCSimilarityException();
4368 alpha_image=SIMCrossCorrelationImage(target_image,reconstruct_image,
4370 target_image=DestroyImage(target_image);
4371 if (alpha_image == (Image *) NULL)
4372 ThrowNCCSimilarityException();
4373 status=SIMMultiplyImage(alpha_image,(
double) QuantumRange*
4374 reconstruct->columns*reconstruct->rows,(
const ChannelStatistics *) NULL,
4376 if (status == MagickFalse)
4377 ThrowNCCSimilarityException();
4381 beta_image=SIMCrossCorrelationImage(image,reconstruct_image,exception);
4382 reconstruct_image=DestroyImage(reconstruct_image);
4383 if (beta_image == (Image *) NULL)
4384 ThrowNCCSimilarityException();
4385 target_image=SIMSquareImage(beta_image,exception);
4386 beta_image=DestroyImage(beta_image);
4387 if (target_image == (Image *) NULL)
4388 ThrowNCCSimilarityException();
4389 status=SIMMultiplyImage(target_image,(
double) QuantumRange,
4390 (
const ChannelStatistics *) NULL,exception);
4391 if (status == MagickFalse)
4392 ThrowNCCSimilarityException();
4396 variance_image=SIMVarianceImage(alpha_image,target_image,exception);
4397 target_image=DestroyImage(target_image);
4398 alpha_image=DestroyImage(alpha_image);
4399 if (variance_image == (Image *) NULL)
4400 ThrowNCCSimilarityException();
4404 channel_statistics=GetImageStatistics(reconstruct,exception);
4405 if (channel_statistics == (ChannelStatistics *) NULL)
4406 ThrowNCCSimilarityException();
4407 status=SIMMultiplyImage(variance_image,1.0,channel_statistics,exception);
4408 if (status == MagickFalse)
4409 ThrowNCCSimilarityException();
4410 normalize_image=SIMSubtractImageMean(image,reconstruct,channel_statistics,
4412 channel_statistics=(ChannelStatistics *)
4413 RelinquishMagickMemory(channel_statistics);
4414 if (normalize_image == (Image *) NULL)
4415 ThrowNCCSimilarityException();
4416 correlation_image=SIMCrossCorrelationImage(image,normalize_image,exception);
4417 normalize_image=DestroyImage(normalize_image);
4418 if (correlation_image == (Image *) NULL)
4419 ThrowNCCSimilarityException();
4423 divide_image=SIMDivideImage(correlation_image,variance_image,exception);
4424 correlation_image=DestroyImage(correlation_image);
4425 variance_image=DestroyImage(variance_image);
4426 if (divide_image == (Image *) NULL)
4427 ThrowNCCSimilarityException();
4431 SetGeometry(image,&geometry);
4432 geometry.width=image->columns;
4433 geometry.height=image->rows;
4434 (void) ResetImagePage(divide_image,
"0x0+0+0");
4435 ncc_image=CropImage(divide_image,&geometry,exception);
4436 divide_image=DestroyImage(divide_image);
4437 if (ncc_image == (Image *) NULL)
4438 ThrowNCCSimilarityException();
4442 (void) ResetImagePage(ncc_image,
"0x0+0+0");
4443 status=GrayscaleImage(ncc_image,AveragePixelIntensityMethod,exception);
4444 if (status == MagickFalse)
4445 ThrowNCCSimilarityException();
4446 ncc_image->depth=32;
4447 ncc_image->colorspace=GRAYColorspace;
4448 ncc_image->alpha_trait=UndefinedPixelTrait;
4449 status=SIMMaximaImage(ncc_image,&maxima,offset,exception);
4450 if (status == MagickFalse)
4451 ThrowNCCSimilarityException();
4452 if ((QuantumScale*maxima) > 1.0)
4454 status=SIMMultiplyImage(ncc_image,1.0/(QuantumScale*maxima),
4455 (
const ChannelStatistics *) NULL,exception);
4456 maxima=(double) QuantumRange;
4458 *similarity_metric=QuantumScale*maxima;
4462static Image *PhaseSimilarityImage(
const Image *image,
const Image *reconstruct,
4463 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4465#define ThrowPhaseSimilarityException() \
4467 if (correlation_image != (Image *) NULL) \
4468 correlation_image=DestroyImage(correlation_image); \
4469 if (fft_images != (Image *) NULL) \
4470 fft_images=DestroyImageList(fft_images); \
4471 if (gamma_image != (Image *) NULL) \
4472 gamma_image=DestroyImage(gamma_image); \
4473 if (magnitude_image != (Image *) NULL) \
4474 magnitude_image=DestroyImage(magnitude_image); \
4475 if (phase_image != (Image *) NULL) \
4476 phase_image=DestroyImage(phase_image); \
4477 if (reconstruct_image != (Image *) NULL) \
4478 reconstruct_image=DestroyImage(reconstruct_image); \
4479 if (reconstruct_magnitude != (Image *) NULL) \
4480 reconstruct_magnitude=DestroyImage(reconstruct_magnitude); \
4481 if (target_image != (Image *) NULL) \
4482 target_image=DestroyImage(target_image); \
4483 if (test_magnitude != (Image *) NULL) \
4484 test_magnitude=DestroyImage(test_magnitude); \
4485 return((Image *) NULL); \
4492 *correlation_image = (Image *) NULL,
4493 *fft_images = (Image *) NULL,
4494 *gamma_image = (Image *) NULL,
4495 *magnitude_image = (Image *) NULL,
4496 *phase_image = (Image *) NULL,
4497 *reconstruct_image = (Image *) NULL,
4498 *reconstruct_magnitude = (Image *) NULL,
4499 *target_image = (Image *) NULL,
4500 *test_magnitude = (Image *) NULL;
4503 status = MagickTrue;
4511 target_image=CloneImage(image,0,0,MagickTrue,exception);
4512 if (target_image == (Image *) NULL)
4513 ThrowPhaseSimilarityException();
4514 (void) ResetImagePage(target_image,
"0x0+0+0");
4515 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4516 &target_image->background_color);
4517 status=SetImageExtent(target_image,2*(
size_t) ceil((
double) image->columns/
4518 2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4519 if (status == MagickFalse)
4520 ThrowPhaseSimilarityException();
4524 reconstruct_image=CloneImage(reconstruct,0,0,MagickTrue,exception);
4525 if (reconstruct_image == (Image *) NULL)
4526 ThrowPhaseSimilarityException();
4527 (void) ResetImagePage(reconstruct_image,
"0x0+0+0");
4528 GetPixelInfoRGBA((Quantum) 0,(Quantum) 0,(Quantum) 0,(Quantum) 0,
4529 &reconstruct_image->background_color);
4530 status=SetImageExtent(reconstruct_image,2*(
size_t) ceil((
double)
4531 image->columns/2.0),2*(
size_t) ceil((
double) image->rows/2.0),exception);
4532 if (status == MagickFalse)
4533 ThrowPhaseSimilarityException();
4537 (void) SetImageArtifact(target_image,
"fourier:normalize",
"inverse");
4538 fft_images=ForwardFourierTransformImage(target_image,MagickTrue,exception);
4539 if (fft_images == (Image *) NULL)
4540 ThrowPhaseSimilarityException();
4541 test_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4542 fft_images=DestroyImageList(fft_images);
4543 if (test_magnitude == (Image *) NULL)
4544 ThrowPhaseSimilarityException();
4545 (void) SetImageArtifact(reconstruct_image,
"fourier:normalize",
"inverse");
4546 fft_images=ForwardFourierTransformImage(reconstruct_image,MagickTrue,
4548 if (fft_images == (Image *) NULL)
4549 ThrowPhaseSimilarityException();
4550 reconstruct_magnitude=CloneImage(fft_images,0,0,MagickTrue,exception);
4551 fft_images=DestroyImageList(fft_images);
4552 if (reconstruct_magnitude == (Image *) NULL)
4553 ThrowPhaseSimilarityException();
4554 magnitude_image=CloneImage(reconstruct_magnitude,0,0,MagickTrue,exception);
4555 if (magnitude_image == (Image *) NULL)
4556 ThrowPhaseSimilarityException();
4557 DisableCompositeClampUnlessSpecified(magnitude_image);
4558 (void) CompositeImage(magnitude_image,test_magnitude,MultiplyCompositeOp,
4559 MagickTrue,0,0,exception);
4563 correlation_image=SIMPhaseCorrelationImage(target_image,reconstruct_image,
4564 magnitude_image,exception);
4565 target_image=DestroyImage(target_image);
4566 reconstruct_image=DestroyImage(reconstruct_image);
4567 test_magnitude=DestroyImage(test_magnitude);
4568 reconstruct_magnitude=DestroyImage(reconstruct_magnitude);
4569 if (correlation_image == (Image *) NULL)
4570 ThrowPhaseSimilarityException();
4574 gamma_image=CloneImage(correlation_image,0,0,MagickTrue,exception);
4575 correlation_image=DestroyImage(correlation_image);
4576 if (gamma_image == (Image *) NULL)
4577 ThrowPhaseSimilarityException();
4581 SetGeometry(image,&geometry);
4582 geometry.width=image->columns;
4583 geometry.height=image->rows;
4584 (void) ResetImagePage(gamma_image,
"0x0+0+0");
4585 phase_image=CropImage(gamma_image,&geometry,exception);
4586 gamma_image=DestroyImage(gamma_image);
4587 if (phase_image == (Image *) NULL)
4588 ThrowPhaseSimilarityException();
4589 (void) ResetImagePage(phase_image,
"0x0+0+0");
4593 status=GrayscaleImage(phase_image,AveragePixelIntensityMethod,exception);
4594 if (status == MagickFalse)
4595 ThrowPhaseSimilarityException();
4596 phase_image->depth=32;
4597 phase_image->colorspace=GRAYColorspace;
4598 phase_image->alpha_trait=UndefinedPixelTrait;
4599 status=SIMFilterImageNaNs(phase_image,exception);
4600 if (status == MagickFalse)
4601 ThrowPhaseSimilarityException();
4602 status=SIMMaximaImage(phase_image,&maxima,offset,exception);
4603 if (status == MagickFalse)
4604 ThrowPhaseSimilarityException();
4605 magnitude_image=DestroyImage(magnitude_image);
4606 if ((QuantumScale*maxima) > 1.0)
4608 status=SIMMultiplyImage(phase_image,1.0/(QuantumScale*maxima),
4609 (
const ChannelStatistics *) NULL,exception);
4610 maxima=(double) QuantumRange;
4612 *similarity_metric=QuantumScale*maxima;
4613 return(phase_image);
4616static Image *PSNRSimilarityImage(
const Image *image,
const Image *reconstruct,
4617 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4620 *psnr_image = (Image *) NULL;
4622 psnr_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4624 if (psnr_image == (Image *) NULL)
4626 *similarity_metric=10.0*MagickSafeLog10(MagickSafeReciprocal(
4627 *similarity_metric))/MagickSafePSNRRecipicol(10.0);
4631static Image *RMSESimilarityImage(
const Image *image,
const Image *reconstruct,
4632 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4635 *rmse_image = (Image *) NULL;
4637 rmse_image=MSESimilarityImage(image,reconstruct,offset,similarity_metric,
4639 if (rmse_image == (Image *) NULL)
4641 *similarity_metric=sqrt(*similarity_metric);
4646static double GetSimilarityMetric(
const Image *image,
4647 const Image *reconstruct_image,
const MetricType metric,
4648 const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
4651 *channel_similarity,
4655 *sans_exception = AcquireExceptionInfo();
4661 status = MagickTrue;
4667 length = MaxPixelChannels+1UL;
4669 SetGeometry(reconstruct_image,&geometry);
4670 geometry.x=x_offset;
4671 geometry.y=y_offset;
4672 similarity_image=CropImage(image,&geometry,sans_exception);
4673 sans_exception=DestroyExceptionInfo(sans_exception);
4674 if (similarity_image == (Image *) NULL)
4679 channel_similarity=(
double *) AcquireQuantumMemory(length,
4680 sizeof(*channel_similarity));
4681 if (channel_similarity == (
double *) NULL)
4682 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
4683 (void) memset(channel_similarity,0,length*
sizeof(*channel_similarity));
4686 case AbsoluteErrorMetric:
4688 status=GetAESimilarity(similarity_image,reconstruct_image,
4689 channel_similarity,exception);
4692 case DotProductCorrelationErrorMetric:
4694 status=GetDPCSimilarity(similarity_image,reconstruct_image,
4695 channel_similarity,exception);
4698 case FuzzErrorMetric:
4700 status=GetFUZZSimilarity(similarity_image,reconstruct_image,
4701 channel_similarity,exception);
4704 case MeanAbsoluteErrorMetric:
4706 status=GetMAESimilarity(similarity_image,reconstruct_image,
4707 channel_similarity,exception);
4710 case MeanErrorPerPixelErrorMetric:
4712 status=GetMEPPSimilarity(similarity_image,reconstruct_image,
4713 channel_similarity,exception);
4716 case MeanSquaredErrorMetric:
4718 status=GetMSESimilarity(similarity_image,reconstruct_image,
4719 channel_similarity,exception);
4722 case NormalizedCrossCorrelationErrorMetric:
4724 status=GetNCCSimilarity(similarity_image,reconstruct_image,
4725 channel_similarity,exception);
4728 case PeakAbsoluteErrorMetric:
4730 status=GetPASimilarity(similarity_image,reconstruct_image,
4731 channel_similarity,exception);
4734 case PeakSignalToNoiseRatioErrorMetric:
4736 status=GetPSNRSimilarity(similarity_image,reconstruct_image,
4737 channel_similarity,exception);
4740 case PerceptualHashErrorMetric:
4742 status=GetPHASHSimilarity(similarity_image,reconstruct_image,
4743 channel_similarity,exception);
4746 case PhaseCorrelationErrorMetric:
4748 status=GetPHASESimilarity(similarity_image,reconstruct_image,
4749 channel_similarity,exception);
4752 case PixelDifferenceCountErrorMetric:
4754 status=GetPDCSimilarity(similarity_image,reconstruct_image,
4755 channel_similarity,exception);
4758 case RootMeanSquaredErrorMetric:
4759 case UndefinedErrorMetric:
4762 status=GetRMSESimilarity(similarity_image,reconstruct_image,
4763 channel_similarity,exception);
4766 case StructuralDissimilarityErrorMetric:
4768 status=GetDSSIMSimilarity(similarity_image,reconstruct_image,
4769 channel_similarity,exception);
4772 case StructuralSimilarityErrorMetric:
4774 status=GetSSIMSimularity(similarity_image,reconstruct_image,
4775 channel_similarity,exception);
4779 similarity_image=DestroyImage(similarity_image);
4780 similarity=channel_similarity[CompositePixelChannel];
4781 channel_similarity=(
double *) RelinquishMagickMemory(channel_similarity);
4782 if (status == MagickFalse)
4787MagickExport Image *SimilarityImage(
const Image *image,
const Image *reconstruct,
4788 const MetricType metric,
const double similarity_threshold,
4789 RectangleInfo *offset,
double *similarity_metric,ExceptionInfo *exception)
4791#define SimilarityImageTag "Similarity/Image"
4807 *similarity_image = (Image *) NULL;
4810 status = MagickTrue;
4816 similarity_info = { 0.0, 0, 0 };
4825 assert(image != (
const Image *) NULL);
4826 assert(image->signature == MagickCoreSignature);
4827 assert(exception != (ExceptionInfo *) NULL);
4828 assert(exception->signature == MagickCoreSignature);
4829 assert(offset != (RectangleInfo *) NULL);
4830 if (IsEventLogging() != MagickFalse)
4831 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4832 SetGeometry(reconstruct,offset);
4833 *similarity_metric=0.0;
4836#if defined(MAGICKCORE_HDRI_SUPPORT) && defined(MAGICKCORE_FFTW_DELEGATE)
4838 const char *artifact = GetImageArtifact(image,
"compare:frequency-domain");
4839 if (artifact == (
const char *) NULL)
4840 artifact=GetImageArtifact(image,
"compare:accelerate-ncc");
4841 if (((artifact == (
const char *) NULL) ||
4842 (IsStringTrue(artifact) != MagickFalse)) &&
4843 ((image->channels & ReadMaskChannel) == 0))
4846 case DotProductCorrelationErrorMetric:
4848 similarity_image=DPCSimilarityImage(image,reconstruct,offset,
4849 similarity_metric,exception);
4850 return(similarity_image);
4852 case MeanSquaredErrorMetric:
4854 similarity_image=MSESimilarityImage(image,reconstruct,offset,
4855 similarity_metric,exception);
4856 return(similarity_image);
4858 case NormalizedCrossCorrelationErrorMetric:
4860 similarity_image=NCCSimilarityImage(image,reconstruct,offset,
4861 similarity_metric,exception);
4862 return(similarity_image);
4864 case PeakSignalToNoiseRatioErrorMetric:
4866 similarity_image=PSNRSimilarityImage(image,reconstruct,offset,
4867 similarity_metric,exception);
4868 return(similarity_image);
4870 case PhaseCorrelationErrorMetric:
4872 similarity_image=PhaseSimilarityImage(image,reconstruct,offset,
4873 similarity_metric,exception);
4874 return(similarity_image);
4876 case RootMeanSquaredErrorMetric:
4877 case UndefinedErrorMetric:
4879 similarity_image=RMSESimilarityImage(image,reconstruct,offset,
4880 similarity_metric,exception);
4881 return(similarity_image);
4888 if ((image->columns < reconstruct->columns) ||
4889 (image->rows < reconstruct->rows))
4891 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
4892 "GeometryDoesNotContainImage",
"`%s'",image->filename);
4893 return((Image *) NULL);
4895 SetImageCompareBounds(image,reconstruct,&columns,&rows);
4896 similarity_image=CloneImage(image,columns,rows,MagickTrue,exception);
4897 if (similarity_image == (Image *) NULL)
4898 return((Image *) NULL);
4899 similarity_image->depth=32;
4900 similarity_image->colorspace=GRAYColorspace;
4901 similarity_image->alpha_trait=UndefinedPixelTrait;
4902 status=SetImageStorageClass(similarity_image,DirectClass,exception);
4903 if (status == MagickFalse)
4904 return(DestroyImage(similarity_image));
4908 similarity_info.similarity=GetSimilarityMetric(image,reconstruct,metric,
4909 similarity_info.x,similarity_info.y,exception);
4910 similarity_view=AcquireAuthenticCacheView(similarity_image,exception);
4911#if defined(MAGICKCORE_OPENMP_SUPPORT)
4912 #pragma omp parallel for schedule(static) shared(similarity_info,status) \
4913 magick_number_threads(image,reconstruct,similarity_image->rows,1)
4915 for (y=0; y < (ssize_t) similarity_image->rows; y++)
4921 threshold_trigger = MagickFalse;
4927 channel_info = similarity_info;
4932 if (status == MagickFalse)
4934 if (threshold_trigger != MagickFalse)
4936 q=QueueCacheViewAuthenticPixels(similarity_view,0,y,
4937 similarity_image->columns,1,exception);
4938 if (q == (Quantum *) NULL)
4943 for (x=0; x < (ssize_t) similarity_image->columns; x++)
4948 similarity=GetSimilarityMetric((Image *) image,reconstruct,metric,x,y,
4952 case DotProductCorrelationErrorMetric:
4953 case NormalizedCrossCorrelationErrorMetric:
4954 case PeakSignalToNoiseRatioErrorMetric:
4955 case PhaseCorrelationErrorMetric:
4956 case StructuralSimilarityErrorMetric:
4958 if (similarity <= channel_info.similarity)
4960 channel_info.similarity=similarity;
4967 if (similarity >= channel_info.similarity)
4969 channel_info.similarity=similarity;
4975 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4977 PixelChannel channel = GetPixelChannelChannel(image,i);
4978 PixelTrait traits = GetPixelChannelTraits(image,channel);
4979 PixelTrait similarity_traits = GetPixelChannelTraits(similarity_image,
4981 if (((traits & UpdatePixelTrait) == 0) ||
4982 ((similarity_traits & UpdatePixelTrait) == 0))
4986 case DotProductCorrelationErrorMetric:
4987 case NormalizedCrossCorrelationErrorMetric:
4988 case PeakSignalToNoiseRatioErrorMetric:
4989 case PhaseCorrelationErrorMetric:
4990 case StructuralSimilarityErrorMetric:
4992 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4993 QuantumRange*similarity),q);
4998 SetPixelChannel(similarity_image,channel,ClampToQuantum((
double)
4999 QuantumRange*(1.0-similarity)),q);
5004 q+=(ptrdiff_t) GetPixelChannels(similarity_image);
5006#if defined(MAGICKCORE_OPENMP_SUPPORT)
5007 #pragma omp critical (MagickCore_GetSimilarityMetric)
5011 case DotProductCorrelationErrorMetric:
5012 case NormalizedCrossCorrelationErrorMetric:
5013 case PeakSignalToNoiseRatioErrorMetric:
5014 case PhaseCorrelationErrorMetric:
5015 case StructuralSimilarityErrorMetric:
5017 if (similarity_threshold != DefaultSimilarityThreshold)
5018 if (channel_info.similarity >= similarity_threshold)
5019 threshold_trigger=MagickTrue;
5020 if (channel_info.similarity >= similarity_info.similarity)
5021 similarity_info=channel_info;
5026 if (similarity_threshold != DefaultSimilarityThreshold)
5027 if (channel_info.similarity < similarity_threshold)
5028 threshold_trigger=MagickTrue;
5029 if (channel_info.similarity < similarity_info.similarity)
5030 similarity_info=channel_info;
5034 if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
5036 if (image->progress_monitor != (MagickProgressMonitor) NULL)
5042 proceed=SetImageProgress(image,SimilarityImageTag,progress,image->rows);
5043 if (proceed == MagickFalse)
5047 similarity_view=DestroyCacheView(similarity_view);
5048 if (status == MagickFalse)
5049 similarity_image=DestroyImage(similarity_image);
5050 *similarity_metric=similarity_info.similarity;
5051 if (fabs(*similarity_metric) < MagickEpsilon)
5052 *similarity_metric=0.0;
5053 offset->x=similarity_info.x;
5054 offset->y=similarity_info.y;
5055 (void) FormatImageProperty((Image *) image,
"similarity",
"%.*g",
5056 GetMagickPrecision(),*similarity_metric);
5057 (void) FormatImageProperty((Image *) image,
"similarity.offset.x",
"%.*g",
5058 GetMagickPrecision(),(
double) offset->x);
5059 (void) FormatImageProperty((Image *) image,
"similarity.offset.y",
"%.*g",
5060 GetMagickPrecision(),(
double) offset->y);
5061 return(similarity_image);