2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % SSSSS TTTTT AAA TTTTT IIIII SSSSS TTTTT IIIII CCCC %
7 % SS T A A T I SS T I C %
8 % SSS T AAAAA T I SSS T I C %
9 % SS T A A T I SS T I C %
10 % SSSSS T A A T IIIII SSSSS T IIIII CCCC %
13 % MagickCore Image Statistical Methods %
20 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/accelerate-private.h"
45 #include "MagickCore/animate.h"
46 #include "MagickCore/artifact.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/cache-private.h"
51 #include "MagickCore/cache-view.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colorspace.h"
56 #include "MagickCore/colorspace-private.h"
57 #include "MagickCore/composite.h"
58 #include "MagickCore/composite-private.h"
59 #include "MagickCore/compress.h"
60 #include "MagickCore/constitute.h"
61 #include "MagickCore/display.h"
62 #include "MagickCore/draw.h"
63 #include "MagickCore/enhance.h"
64 #include "MagickCore/exception.h"
65 #include "MagickCore/exception-private.h"
66 #include "MagickCore/gem.h"
67 #include "MagickCore/gem-private.h"
68 #include "MagickCore/geometry.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/image-private.h"
71 #include "MagickCore/magic.h"
72 #include "MagickCore/magick.h"
73 #include "MagickCore/memory_.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/paint.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantize.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/random_.h"
85 #include "MagickCore/random-private.h"
86 #include "MagickCore/resource_.h"
87 #include "MagickCore/segment.h"
88 #include "MagickCore/semaphore.h"
89 #include "MagickCore/signature-private.h"
90 #include "MagickCore/statistic.h"
91 #include "MagickCore/string_.h"
92 #include "MagickCore/thread-private.h"
93 #include "MagickCore/timer.h"
94 #include "MagickCore/utility.h"
95 #include "MagickCore/version.h"
98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 % E v a l u a t e I m a g e %
106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108 % EvaluateImage() applies a value to the image with an arithmetic, relational,
109 % or logical operator to an image. Use these operations to lighten or darken
110 % an image, to increase or decrease contrast in an image, or to produce the
111 % "negative" of an image.
113 % The format of the EvaluateImage method is:
115 % MagickBooleanType EvaluateImage(Image *image,
116 % const MagickEvaluateOperator op,const double value,
117 % ExceptionInfo *exception)
118 % MagickBooleanType EvaluateImages(Image *images,
119 % const MagickEvaluateOperator op,const double value,
120 % ExceptionInfo *exception)
122 % A description of each parameter follows:
124 % o image: the image.
126 % o op: A channel op.
128 % o value: A value value.
130 % o exception: return any errors or warnings in this structure.
134 typedef struct _PixelChannels
137 channel[CompositePixelChannel];
140 static PixelChannels **DestroyPixelThreadSet(PixelChannels **pixels)
145 assert(pixels != (PixelChannels **) NULL);
146 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
147 if (pixels[i] != (PixelChannels *) NULL)
148 pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]);
149 pixels=(PixelChannels **) RelinquishMagickMemory(pixels);
153 static PixelChannels **AcquirePixelThreadSet(const Image *image)
164 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
165 pixels=(PixelChannels **) AcquireQuantumMemory(number_threads,
167 if (pixels == (PixelChannels **) NULL)
168 return((PixelChannels **) NULL);
169 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
170 for (i=0; i < (ssize_t) number_threads; i++)
175 pixels[i]=(PixelChannels *) AcquireQuantumMemory(image->columns,
177 if (pixels[i] == (PixelChannels *) NULL)
178 return(DestroyPixelThreadSet(pixels));
179 for (j=0; j < (ssize_t) image->columns; j++)
184 for (k=0; k < MaxPixelChannels; k++)
185 pixels[i][j].channel[k]=0.0;
191 static inline double EvaluateMax(const double x,const double y)
198 #if defined(__cplusplus) || defined(c_plusplus)
202 static int IntensityCompare(const void *x,const void *y)
214 color_1=(const PixelChannels *) x;
215 color_2=(const PixelChannels *) y;
217 for (i=0; i < MaxPixelChannels; i++)
218 distance+=color_1->channel[i]-(double) color_2->channel[i];
219 return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
222 #if defined(__cplusplus) || defined(c_plusplus)
226 static double ApplyEvaluateOperator(RandomInfo *random_info,const Quantum pixel,
227 const MagickEvaluateOperator op,const double value)
235 case UndefinedEvaluateOperator:
237 case AbsEvaluateOperator:
239 result=(double) fabs((double) (pixel+value));
242 case AddEvaluateOperator:
244 result=(double) (pixel+value);
247 case AddModulusEvaluateOperator:
250 This returns a 'floored modulus' of the addition which is a positive
251 result. It differs from % or fmod() that returns a 'truncated modulus'
252 result, where floor() is replaced by trunc() and could return a
253 negative result (which is clipped).
256 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
259 case AndEvaluateOperator:
261 result=(double) ((size_t) pixel & (size_t) (value+0.5));
264 case CosineEvaluateOperator:
266 result=(double) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
267 QuantumScale*pixel*value))+0.5));
270 case DivideEvaluateOperator:
272 result=pixel/(value == 0.0 ? 1.0 : value);
275 case ExponentialEvaluateOperator:
277 result=(double) (QuantumRange*exp((double) (value*QuantumScale*pixel)));
280 case GaussianNoiseEvaluateOperator:
282 result=(double) GenerateDifferentialNoise(random_info,pixel,
283 GaussianNoise,value);
286 case ImpulseNoiseEvaluateOperator:
288 result=(double) GenerateDifferentialNoise(random_info,pixel,ImpulseNoise,
292 case LaplacianNoiseEvaluateOperator:
294 result=(double) GenerateDifferentialNoise(random_info,pixel,
295 LaplacianNoise,value);
298 case LeftShiftEvaluateOperator:
300 result=(double) ((size_t) pixel << (size_t) (value+0.5));
303 case LogEvaluateOperator:
305 if ((QuantumScale*pixel) >= MagickEpsilon)
306 result=(double) (QuantumRange*log((double) (QuantumScale*value*pixel+
307 1.0))/log((double) (value+1.0)));
310 case MaxEvaluateOperator:
312 result=(double) EvaluateMax((double) pixel,value);
315 case MeanEvaluateOperator:
317 result=(double) (pixel+value);
320 case MedianEvaluateOperator:
322 result=(double) (pixel+value);
325 case MinEvaluateOperator:
327 result=(double) MagickMin((double) pixel,value);
330 case MultiplicativeNoiseEvaluateOperator:
332 result=(double) GenerateDifferentialNoise(random_info,pixel,
333 MultiplicativeGaussianNoise,value);
336 case MultiplyEvaluateOperator:
338 result=(double) (value*pixel);
341 case OrEvaluateOperator:
343 result=(double) ((size_t) pixel | (size_t) (value+0.5));
346 case PoissonNoiseEvaluateOperator:
348 result=(double) GenerateDifferentialNoise(random_info,pixel,PoissonNoise,
352 case PowEvaluateOperator:
354 result=(double) (QuantumRange*pow((double) (QuantumScale*pixel),(double)
358 case RightShiftEvaluateOperator:
360 result=(double) ((size_t) pixel >> (size_t) (value+0.5));
363 case RootMeanSquareEvaluateOperator:
365 result=(double) (pixel*pixel+value);
368 case SetEvaluateOperator:
373 case SineEvaluateOperator:
375 result=(double) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
376 QuantumScale*pixel*value))+0.5));
379 case SubtractEvaluateOperator:
381 result=(double) (pixel-value);
384 case SumEvaluateOperator:
386 result=(double) (pixel+value);
389 case ThresholdEvaluateOperator:
391 result=(double) (((double) pixel <= value) ? 0 : QuantumRange);
394 case ThresholdBlackEvaluateOperator:
396 result=(double) (((double) pixel <= value) ? 0 : pixel);
399 case ThresholdWhiteEvaluateOperator:
401 result=(double) (((double) pixel > value) ? QuantumRange : pixel);
404 case UniformNoiseEvaluateOperator:
406 result=(double) GenerateDifferentialNoise(random_info,pixel,UniformNoise,
410 case XorEvaluateOperator:
412 result=(double) ((size_t) pixel ^ (size_t) (value+0.5));
419 MagickExport Image *EvaluateImages(const Image *images,
420 const MagickEvaluateOperator op,ExceptionInfo *exception)
422 #define EvaluateImageTag "Evaluate/Image"
437 **magick_restrict evaluate_pixels;
440 **magick_restrict random_info;
448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
453 assert(images != (Image *) NULL);
454 assert(images->signature == MagickCoreSignature);
455 if (images->debug != MagickFalse)
456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
457 assert(exception != (ExceptionInfo *) NULL);
458 assert(exception->signature == MagickCoreSignature);
459 image=CloneImage(images,images->columns,images->rows,MagickTrue,
461 if (image == (Image *) NULL)
462 return((Image *) NULL);
463 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
465 image=DestroyImage(image);
466 return((Image *) NULL);
468 number_images=GetImageListLength(images);
469 evaluate_pixels=AcquirePixelThreadSet(images);
470 if (evaluate_pixels == (PixelChannels **) NULL)
472 image=DestroyImage(image);
473 (void) ThrowMagickException(exception,GetMagickModule(),
474 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
475 return((Image *) NULL);
478 Evaluate image pixels.
482 random_info=AcquireRandomInfoThreadSet();
483 evaluate_view=AcquireAuthenticCacheView(image,exception);
484 if (op == MedianEvaluateOperator)
486 #if defined(MAGICKCORE_OPENMP_SUPPORT)
487 key=GetRandomSecretKey(random_info[0]);
488 #pragma omp parallel for schedule(static,4) shared(progress,status) \
489 magick_threads(image,images,image->rows,key == ~0UL)
491 for (y=0; y < (ssize_t) image->rows; y++)
500 id = GetOpenMPThreadId();
502 register PixelChannels
511 if (status == MagickFalse)
513 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
515 if (q == (Quantum *) NULL)
520 evaluate_pixel=evaluate_pixels[id];
521 for (x=0; x < (ssize_t) image->columns; x++)
527 for (j=0; j < (ssize_t) number_images; j++)
528 for (k=0; k < MaxPixelChannels; k++)
529 evaluate_pixel[j].channel[k]=0.0;
531 for (j=0; j < (ssize_t) number_images; j++)
533 register const Quantum
539 image_view=AcquireVirtualCacheView(next,exception);
540 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
541 if (p == (const Quantum *) NULL)
543 image_view=DestroyCacheView(image_view);
546 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
548 PixelChannel channel=GetPixelChannelChannel(image,i);
549 PixelTrait evaluate_traits=GetPixelChannelTraits(image,channel);
550 PixelTrait traits=GetPixelChannelTraits(next,channel);
551 if ((traits == UndefinedPixelTrait) ||
552 (evaluate_traits == UndefinedPixelTrait))
554 if ((evaluate_traits & UpdatePixelTrait) == 0)
556 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(
557 random_info[id],GetPixelChannel(image,channel,p),op,
558 evaluate_pixel[j].channel[i]);
560 image_view=DestroyCacheView(image_view);
561 next=GetNextImageInList(next);
563 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
565 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
566 q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]);
567 q+=GetPixelChannels(image);
569 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
571 if (images->progress_monitor != (MagickProgressMonitor) NULL)
576 #if defined(MAGICKCORE_OPENMP_SUPPORT)
577 #pragma omp critical (MagickCore_EvaluateImages)
579 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
581 if (proceed == MagickFalse)
588 #if defined(MAGICKCORE_OPENMP_SUPPORT)
589 key=GetRandomSecretKey(random_info[0]);
590 #pragma omp parallel for schedule(static,4) shared(progress,status) \
591 magick_threads(image,images,image->rows,key == ~0UL)
593 for (y=0; y < (ssize_t) image->rows; y++)
602 id = GetOpenMPThreadId();
608 register PixelChannels
617 if (status == MagickFalse)
619 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
621 if (q == (Quantum *) NULL)
626 evaluate_pixel=evaluate_pixels[id];
627 for (j=0; j < (ssize_t) image->columns; j++)
628 for (i=0; i < MaxPixelChannels; i++)
629 evaluate_pixel[j].channel[i]=0.0;
631 for (j=0; j < (ssize_t) number_images; j++)
633 register const Quantum
636 image_view=AcquireVirtualCacheView(next,exception);
637 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
639 if (p == (const Quantum *) NULL)
641 image_view=DestroyCacheView(image_view);
644 for (x=0; x < (ssize_t) image->columns; x++)
649 if (GetPixelWriteMask(next,p) == 0)
651 p+=GetPixelChannels(next);
654 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
656 PixelChannel channel=GetPixelChannelChannel(image,i);
657 PixelTrait traits=GetPixelChannelTraits(next,channel);
658 PixelTrait evaluate_traits=GetPixelChannelTraits(image,channel);
659 if ((traits == UndefinedPixelTrait) ||
660 (evaluate_traits == UndefinedPixelTrait))
662 if ((traits & UpdatePixelTrait) == 0)
664 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
665 random_info[id],GetPixelChannel(image,channel,p),j == 0 ?
666 AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
668 p+=GetPixelChannels(next);
670 image_view=DestroyCacheView(image_view);
671 next=GetNextImageInList(next);
673 for (x=0; x < (ssize_t) image->columns; x++)
680 case MeanEvaluateOperator:
682 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
683 evaluate_pixel[x].channel[i]/=(double) number_images;
686 case MultiplyEvaluateOperator:
688 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
693 for (j=0; j < (ssize_t) (number_images-1); j++)
694 evaluate_pixel[x].channel[i]*=QuantumScale;
698 case RootMeanSquareEvaluateOperator:
700 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
701 evaluate_pixel[x].channel[i]=sqrt(evaluate_pixel[x].channel[i]/
709 for (x=0; x < (ssize_t) image->columns; x++)
714 if (GetPixelWriteMask(image,q) == 0)
716 q+=GetPixelChannels(image);
719 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
721 PixelChannel channel=GetPixelChannelChannel(image,i);
722 PixelTrait traits=GetPixelChannelTraits(image,channel);
723 if (traits == UndefinedPixelTrait)
725 if ((traits & UpdatePixelTrait) == 0)
727 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
729 q+=GetPixelChannels(image);
731 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
733 if (images->progress_monitor != (MagickProgressMonitor) NULL)
738 #if defined(MAGICKCORE_OPENMP_SUPPORT)
739 #pragma omp critical (MagickCore_EvaluateImages)
741 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
743 if (proceed == MagickFalse)
748 evaluate_view=DestroyCacheView(evaluate_view);
749 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
750 random_info=DestroyRandomInfoThreadSet(random_info);
751 if (status == MagickFalse)
752 image=DestroyImage(image);
756 MagickExport MagickBooleanType EvaluateImage(Image *image,
757 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
769 **magick_restrict random_info;
774 #if defined(MAGICKCORE_OPENMP_SUPPORT)
779 assert(image != (Image *) NULL);
780 assert(image->signature == MagickCoreSignature);
781 if (image->debug != MagickFalse)
782 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
783 assert(exception != (ExceptionInfo *) NULL);
784 assert(exception->signature == MagickCoreSignature);
785 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
789 random_info=AcquireRandomInfoThreadSet();
790 image_view=AcquireAuthenticCacheView(image,exception);
791 #if defined(MAGICKCORE_OPENMP_SUPPORT)
792 key=GetRandomSecretKey(random_info[0]);
793 #pragma omp parallel for schedule(static,4) shared(progress,status) \
794 magick_threads(image,image,image->rows,key == ~0UL)
796 for (y=0; y < (ssize_t) image->rows; y++)
799 id = GetOpenMPThreadId();
807 if (status == MagickFalse)
809 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
810 if (q == (Quantum *) NULL)
815 for (x=0; x < (ssize_t) image->columns; x++)
823 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
825 PixelChannel channel=GetPixelChannelChannel(image,i);
826 PixelTrait traits=GetPixelChannelTraits(image,channel);
827 if (traits == UndefinedPixelTrait)
829 if (((traits & CopyPixelTrait) != 0) ||
830 (GetPixelWriteMask(image,q) == 0))
832 result=ApplyEvaluateOperator(random_info[id],q[i],op,value);
833 if (op == MeanEvaluateOperator)
835 q[i]=ClampToQuantum(result);
837 q+=GetPixelChannels(image);
839 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
841 if (image->progress_monitor != (MagickProgressMonitor) NULL)
846 #if defined(MAGICKCORE_OPENMP_SUPPORT)
847 #pragma omp critical (MagickCore_EvaluateImage)
849 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
850 if (proceed == MagickFalse)
854 image_view=DestroyCacheView(image_view);
855 random_info=DestroyRandomInfoThreadSet(random_info);
860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
864 % F u n c t i o n I m a g e %
868 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
870 % FunctionImage() applies a value to the image with an arithmetic, relational,
871 % or logical operator to an image. Use these operations to lighten or darken
872 % an image, to increase or decrease contrast in an image, or to produce the
873 % "negative" of an image.
875 % The format of the FunctionImage method is:
877 % MagickBooleanType FunctionImage(Image *image,
878 % const MagickFunction function,const ssize_t number_parameters,
879 % const double *parameters,ExceptionInfo *exception)
881 % A description of each parameter follows:
883 % o image: the image.
885 % o function: A channel function.
887 % o parameters: one or more parameters.
889 % o exception: return any errors or warnings in this structure.
893 static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
894 const size_t number_parameters,const double *parameters,
895 ExceptionInfo *exception)
907 case PolynomialFunction:
910 Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+
914 for (i=0; i < (ssize_t) number_parameters; i++)
915 result=result*QuantumScale*pixel+parameters[i];
916 result*=QuantumRange;
919 case SinusoidFunction:
928 Sinusoid: frequency, phase, amplitude, bias.
930 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
931 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
932 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
933 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
934 result=(double) (QuantumRange*(amplitude*sin((double) (2.0*
935 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
947 Arcsin (peged at range limits for invalid results): width, center,
950 width=(number_parameters >= 1) ? parameters[0] : 1.0;
951 center=(number_parameters >= 2) ? parameters[1] : 0.5;
952 range=(number_parameters >= 3) ? parameters[2] : 1.0;
953 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
954 result=2.0/width*(QuantumScale*pixel-center);
955 if ( result <= -1.0 )
956 result=bias-range/2.0;
959 result=bias+range/2.0;
961 result=(double) (range/MagickPI*asin((double) result)+bias);
962 result*=QuantumRange;
974 Arctan: slope, center, range, and bias.
976 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
977 center=(number_parameters >= 2) ? parameters[1] : 0.5;
978 range=(number_parameters >= 3) ? parameters[2] : 1.0;
979 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
980 result=(double) (MagickPI*slope*(QuantumScale*pixel-center));
981 result=(double) (QuantumRange*(range/MagickPI*atan((double)
985 case UndefinedFunction:
988 return(ClampToQuantum(result));
991 MagickExport MagickBooleanType FunctionImage(Image *image,
992 const MagickFunction function,const size_t number_parameters,
993 const double *parameters,ExceptionInfo *exception)
995 #define FunctionImageTag "Function/Image "
1009 assert(image != (Image *) NULL);
1010 assert(image->signature == MagickCoreSignature);
1011 if (image->debug != MagickFalse)
1012 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1013 assert(exception != (ExceptionInfo *) NULL);
1014 assert(exception->signature == MagickCoreSignature);
1015 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1016 if (AccelerateFunctionImage(image,function,number_parameters,parameters,
1017 exception) != MagickFalse)
1020 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1021 return(MagickFalse);
1024 image_view=AcquireAuthenticCacheView(image,exception);
1025 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1026 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1027 magick_threads(image,image,image->rows,1)
1029 for (y=0; y < (ssize_t) image->rows; y++)
1037 if (status == MagickFalse)
1039 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1040 if (q == (Quantum *) NULL)
1045 for (x=0; x < (ssize_t) image->columns; x++)
1050 if (GetPixelWriteMask(image,q) == 0)
1052 q+=GetPixelChannels(image);
1055 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1057 PixelChannel channel=GetPixelChannelChannel(image,i);
1058 PixelTrait traits=GetPixelChannelTraits(image,channel);
1059 if (traits == UndefinedPixelTrait)
1061 if ((traits & UpdatePixelTrait) == 0)
1063 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1066 q+=GetPixelChannels(image);
1068 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1070 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1075 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1076 #pragma omp critical (MagickCore_FunctionImage)
1078 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1079 if (proceed == MagickFalse)
1083 image_view=DestroyCacheView(image_view);
1088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092 % G e t I m a g e E n t r o p y %
1096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098 % GetImageEntropy() returns the entropy of one or more image channels.
1100 % The format of the GetImageEntropy method is:
1102 % MagickBooleanType GetImageEntropy(const Image *image,double *entropy,
1103 % ExceptionInfo *exception)
1105 % A description of each parameter follows:
1107 % o image: the image.
1109 % o entropy: the average entropy of the selected channels.
1111 % o exception: return any errors or warnings in this structure.
1114 MagickExport MagickBooleanType GetImageEntropy(const Image *image,
1115 double *entropy,ExceptionInfo *exception)
1121 *channel_statistics;
1126 assert(image != (Image *) NULL);
1127 assert(image->signature == MagickCoreSignature);
1128 if (image->debug != MagickFalse)
1129 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1130 channel_statistics=GetImageStatistics(image,exception);
1131 if (channel_statistics == (ChannelStatistics *) NULL)
1132 return(MagickFalse);
1134 channel_statistics[CompositePixelChannel].entropy=0.0;
1135 channel_statistics[CompositePixelChannel].standard_deviation=0.0;
1136 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1138 PixelChannel channel=GetPixelChannelChannel(image,i);
1139 PixelTrait traits=GetPixelChannelTraits(image,channel);
1140 if (traits == UndefinedPixelTrait)
1142 if ((traits & UpdatePixelTrait) == 0)
1144 channel_statistics[CompositePixelChannel].entropy+=
1145 channel_statistics[i].entropy;
1148 if (area > MagickEpsilon)
1150 channel_statistics[CompositePixelChannel].entropy/=area;
1151 channel_statistics[CompositePixelChannel].standard_deviation=
1152 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1154 *entropy=channel_statistics[CompositePixelChannel].entropy;
1155 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1156 channel_statistics);
1161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165 % G e t I m a g e E x t r e m a %
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 % GetImageExtrema() returns the extrema of one or more image channels.
1173 % The format of the GetImageExtrema method is:
1175 % MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1176 % size_t *maxima,ExceptionInfo *exception)
1178 % A description of each parameter follows:
1180 % o image: the image.
1182 % o minima: the minimum value in the channel.
1184 % o maxima: the maximum value in the channel.
1186 % o exception: return any errors or warnings in this structure.
1189 MagickExport MagickBooleanType GetImageExtrema(const Image *image,
1190 size_t *minima,size_t *maxima,ExceptionInfo *exception)
1199 assert(image != (Image *) NULL);
1200 assert(image->signature == MagickCoreSignature);
1201 if (image->debug != MagickFalse)
1202 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1203 status=GetImageRange(image,&min,&max,exception);
1204 *minima=(size_t) ceil(min-0.5);
1205 *maxima=(size_t) floor(max+0.5);
1210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 % G e t I m a g e K u r t o s i s %
1218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 % GetImageKurtosis() returns the kurtosis and skewness of one or more image
1223 % The format of the GetImageKurtosis method is:
1225 % MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1226 % double *skewness,ExceptionInfo *exception)
1228 % A description of each parameter follows:
1230 % o image: the image.
1232 % o kurtosis: the kurtosis of the channel.
1234 % o skewness: the skewness of the channel.
1236 % o exception: return any errors or warnings in this structure.
1239 MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1240 double *kurtosis,double *skewness,ExceptionInfo *exception)
1259 assert(image != (Image *) NULL);
1260 assert(image->signature == MagickCoreSignature);
1261 if (image->debug != MagickFalse)
1262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1268 standard_deviation=0.0;
1271 sum_fourth_power=0.0;
1272 image_view=AcquireVirtualCacheView(image,exception);
1273 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1274 #pragma omp parallel for schedule(static,4) shared(status) \
1275 magick_threads(image,image,image->rows,1)
1277 for (y=0; y < (ssize_t) image->rows; y++)
1279 register const Quantum
1285 if (status == MagickFalse)
1287 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1288 if (p == (const Quantum *) NULL)
1293 for (x=0; x < (ssize_t) image->columns; x++)
1298 if (GetPixelWriteMask(image,p) == 0)
1300 p+=GetPixelChannels(image);
1303 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1305 PixelChannel channel=GetPixelChannelChannel(image,i);
1306 PixelTrait traits=GetPixelChannelTraits(image,channel);
1307 if (traits == UndefinedPixelTrait)
1309 if ((traits & UpdatePixelTrait) == 0)
1311 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1312 #pragma omp critical (MagickCore_GetImageKurtosis)
1316 sum_squares+=(double) p[i]*p[i];
1317 sum_cubes+=(double) p[i]*p[i]*p[i];
1318 sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
1322 p+=GetPixelChannels(image);
1325 image_view=DestroyCacheView(image_view);
1331 sum_fourth_power/=area;
1333 standard_deviation=sqrt(sum_squares-(mean*mean));
1334 if (standard_deviation != 0.0)
1336 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1337 3.0*mean*mean*mean*mean;
1338 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1341 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1342 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1352 % G e t I m a g e M e a n %
1356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1358 % GetImageMean() returns the mean and standard deviation of one or more image
1361 % The format of the GetImageMean method is:
1363 % MagickBooleanType GetImageMean(const Image *image,double *mean,
1364 % double *standard_deviation,ExceptionInfo *exception)
1366 % A description of each parameter follows:
1368 % o image: the image.
1370 % o mean: the average value in the channel.
1372 % o standard_deviation: the standard deviation of the channel.
1374 % o exception: return any errors or warnings in this structure.
1377 MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1378 double *standard_deviation,ExceptionInfo *exception)
1384 *channel_statistics;
1389 assert(image != (Image *) NULL);
1390 assert(image->signature == MagickCoreSignature);
1391 if (image->debug != MagickFalse)
1392 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1393 channel_statistics=GetImageStatistics(image,exception);
1394 if (channel_statistics == (ChannelStatistics *) NULL)
1395 return(MagickFalse);
1397 channel_statistics[CompositePixelChannel].mean=0.0;
1398 channel_statistics[CompositePixelChannel].standard_deviation=0.0;
1399 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1401 PixelChannel channel=GetPixelChannelChannel(image,i);
1402 PixelTrait traits=GetPixelChannelTraits(image,channel);
1403 if (traits == UndefinedPixelTrait)
1405 if ((traits & UpdatePixelTrait) == 0)
1407 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
1408 channel_statistics[CompositePixelChannel].standard_deviation+=
1409 channel_statistics[i].variance-channel_statistics[i].mean*
1410 channel_statistics[i].mean;
1413 if (area > MagickEpsilon)
1415 channel_statistics[CompositePixelChannel].mean/=area;
1416 channel_statistics[CompositePixelChannel].standard_deviation=
1417 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1419 *mean=channel_statistics[CompositePixelChannel].mean;
1420 *standard_deviation=
1421 channel_statistics[CompositePixelChannel].standard_deviation;
1422 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1423 channel_statistics);
1428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432 % G e t I m a g e M o m e n t s %
1436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438 % GetImageMoments() returns the normalized moments of one or more image
1441 % The format of the GetImageMoments method is:
1443 % ChannelMoments *GetImageMoments(const Image *image,
1444 % ExceptionInfo *exception)
1446 % A description of each parameter follows:
1448 % o image: the image.
1450 % o exception: return any errors or warnings in this structure.
1454 static size_t GetImageChannels(const Image *image)
1463 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1465 PixelChannel channel=GetPixelChannelChannel(image,i);
1466 PixelTrait traits=GetPixelChannelTraits(image,channel);
1467 if ((traits & UpdatePixelTrait) != 0)
1470 return((size_t) (channels == 0 ? 1 : channels));
1473 MagickExport ChannelMoments *GetImageMoments(const Image *image,
1474 ExceptionInfo *exception)
1476 #define MaxNumberImageMoments 8
1485 M00[MaxPixelChannels+1],
1486 M01[MaxPixelChannels+1],
1487 M02[MaxPixelChannels+1],
1488 M03[MaxPixelChannels+1],
1489 M10[MaxPixelChannels+1],
1490 M11[MaxPixelChannels+1],
1491 M12[MaxPixelChannels+1],
1492 M20[MaxPixelChannels+1],
1493 M21[MaxPixelChannels+1],
1494 M22[MaxPixelChannels+1],
1495 M30[MaxPixelChannels+1];
1498 centroid[MaxPixelChannels+1];
1504 assert(image != (Image *) NULL);
1505 assert(image->signature == MagickCoreSignature);
1506 if (image->debug != MagickFalse)
1507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1508 channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1509 sizeof(*channel_moments));
1510 if (channel_moments == (ChannelMoments *) NULL)
1511 return(channel_moments);
1512 (void) ResetMagickMemory(channel_moments,0,(MaxPixelChannels+1)*
1513 sizeof(*channel_moments));
1514 (void) ResetMagickMemory(centroid,0,sizeof(centroid));
1515 (void) ResetMagickMemory(M00,0,sizeof(M00));
1516 (void) ResetMagickMemory(M01,0,sizeof(M01));
1517 (void) ResetMagickMemory(M02,0,sizeof(M02));
1518 (void) ResetMagickMemory(M03,0,sizeof(M03));
1519 (void) ResetMagickMemory(M10,0,sizeof(M10));
1520 (void) ResetMagickMemory(M11,0,sizeof(M11));
1521 (void) ResetMagickMemory(M12,0,sizeof(M12));
1522 (void) ResetMagickMemory(M20,0,sizeof(M20));
1523 (void) ResetMagickMemory(M21,0,sizeof(M21));
1524 (void) ResetMagickMemory(M22,0,sizeof(M22));
1525 (void) ResetMagickMemory(M30,0,sizeof(M30));
1526 image_view=AcquireVirtualCacheView(image,exception);
1527 for (y=0; y < (ssize_t) image->rows; y++)
1529 register const Quantum
1536 Compute center of mass (centroid).
1538 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1539 if (p == (const Quantum *) NULL)
1541 for (x=0; x < (ssize_t) image->columns; x++)
1546 if (GetPixelWriteMask(image,p) == 0)
1548 p+=GetPixelChannels(image);
1551 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1553 PixelChannel channel=GetPixelChannelChannel(image,i);
1554 PixelTrait traits=GetPixelChannelTraits(image,channel);
1555 if (traits == UndefinedPixelTrait)
1557 if ((traits & UpdatePixelTrait) == 0)
1559 M00[channel]+=QuantumScale*p[i];
1560 M00[MaxPixelChannels]+=QuantumScale*p[i];
1561 M10[channel]+=x*QuantumScale*p[i];
1562 M10[MaxPixelChannels]+=x*QuantumScale*p[i];
1563 M01[channel]+=y*QuantumScale*p[i];
1564 M01[MaxPixelChannels]+=y*QuantumScale*p[i];
1566 p+=GetPixelChannels(image);
1569 for (channel=0; channel <= MaxPixelChannels; channel++)
1572 Compute center of mass (centroid).
1574 if (M00[channel] < MagickEpsilon)
1576 M00[channel]+=MagickEpsilon;
1577 centroid[channel].x=(double) image->columns/2.0;
1578 centroid[channel].y=(double) image->rows/2.0;
1581 M00[channel]+=MagickEpsilon;
1582 centroid[channel].x=M10[channel]/M00[channel];
1583 centroid[channel].y=M01[channel]/M00[channel];
1585 for (y=0; y < (ssize_t) image->rows; y++)
1587 register const Quantum
1594 Compute the image moments.
1596 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1597 if (p == (const Quantum *) NULL)
1599 for (x=0; x < (ssize_t) image->columns; x++)
1604 if (GetPixelWriteMask(image,p) == 0)
1606 p+=GetPixelChannels(image);
1609 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1611 PixelChannel channel=GetPixelChannelChannel(image,i);
1612 PixelTrait traits=GetPixelChannelTraits(image,channel);
1613 if (traits == UndefinedPixelTrait)
1615 if ((traits & UpdatePixelTrait) == 0)
1617 M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1619 M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1621 M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1623 M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1625 M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1627 M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1629 M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1630 (y-centroid[channel].y)*QuantumScale*p[i];
1631 M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1632 (y-centroid[channel].y)*QuantumScale*p[i];
1633 M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1634 (y-centroid[channel].y)*QuantumScale*p[i];
1635 M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1636 (y-centroid[channel].y)*QuantumScale*p[i];
1637 M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1638 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1639 M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1640 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1641 M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1642 (x-centroid[channel].x)*QuantumScale*p[i];
1643 M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1644 (x-centroid[channel].x)*QuantumScale*p[i];
1645 M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1646 (y-centroid[channel].y)*QuantumScale*p[i];
1647 M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1648 (y-centroid[channel].y)*QuantumScale*p[i];
1650 p+=GetPixelChannels(image);
1653 M00[MaxPixelChannels]/=GetImageChannels(image);
1654 M01[MaxPixelChannels]/=GetImageChannels(image);
1655 M02[MaxPixelChannels]/=GetImageChannels(image);
1656 M03[MaxPixelChannels]/=GetImageChannels(image);
1657 M10[MaxPixelChannels]/=GetImageChannels(image);
1658 M11[MaxPixelChannels]/=GetImageChannels(image);
1659 M12[MaxPixelChannels]/=GetImageChannels(image);
1660 M20[MaxPixelChannels]/=GetImageChannels(image);
1661 M21[MaxPixelChannels]/=GetImageChannels(image);
1662 M22[MaxPixelChannels]/=GetImageChannels(image);
1663 M30[MaxPixelChannels]/=GetImageChannels(image);
1664 for (channel=0; channel <= MaxPixelChannels; channel++)
1667 Compute elliptical angle, major and minor axes, eccentricity, & intensity.
1669 channel_moments[channel].centroid=centroid[channel];
1670 channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
1671 ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
1672 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1673 channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
1674 ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
1675 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1676 channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
1677 M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
1678 channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
1679 channel_moments[channel].ellipse_axis.y/
1680 (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
1681 channel_moments[channel].ellipse_intensity=M00[channel]/
1682 (MagickPI*channel_moments[channel].ellipse_axis.x*
1683 channel_moments[channel].ellipse_axis.y+MagickEpsilon);
1685 for (channel=0; channel <= MaxPixelChannels; channel++)
1688 Normalize image moments.
1692 M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
1693 M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
1694 M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
1695 M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
1696 M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
1697 M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
1698 M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
1699 M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
1702 image_view=DestroyCacheView(image_view);
1703 for (channel=0; channel <= MaxPixelChannels; channel++)
1706 Compute Hu invariant moments.
1708 channel_moments[channel].invariant[0]=M20[channel]+M02[channel];
1709 channel_moments[channel].invariant[1]=(M20[channel]-M02[channel])*
1710 (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
1711 channel_moments[channel].invariant[2]=(M30[channel]-3.0*M12[channel])*
1712 (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
1713 (3.0*M21[channel]-M03[channel]);
1714 channel_moments[channel].invariant[3]=(M30[channel]+M12[channel])*
1715 (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
1716 (M21[channel]+M03[channel]);
1717 channel_moments[channel].invariant[4]=(M30[channel]-3.0*M12[channel])*
1718 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1719 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1720 (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
1721 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1722 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1723 (M21[channel]+M03[channel]));
1724 channel_moments[channel].invariant[5]=(M20[channel]-M02[channel])*
1725 ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
1726 (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
1727 4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
1728 channel_moments[channel].invariant[6]=(3.0*M21[channel]-M03[channel])*
1729 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1730 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1731 (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
1732 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1733 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1734 (M21[channel]+M03[channel]));
1735 channel_moments[channel].invariant[7]=M11[channel]*((M30[channel]+
1736 M12[channel])*(M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
1737 (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
1738 (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
1740 if (y < (ssize_t) image->rows)
1741 channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1742 return(channel_moments);
1746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1750 % G e t I m a g e C h a n n e l P e r c e p t u a l H a s h %
1754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1756 % GetImagePerceptualHash() returns the perceptual hash of one or more
1759 % The format of the GetImagePerceptualHash method is:
1761 % ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1762 % ExceptionInfo *exception)
1764 % A description of each parameter follows:
1766 % o image: the image.
1768 % o exception: return any errors or warnings in this structure.
1772 static inline double MagickLog10(const double x)
1774 #define Log10Epsilon (1.0e-11)
1776 if (fabs(x) < Log10Epsilon)
1777 return(log10(Log10Epsilon));
1778 return(log10(fabs(x)));
1781 MagickExport ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1782 ExceptionInfo *exception)
1784 ChannelPerceptualHash
1803 perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1804 MaxPixelChannels+1UL,sizeof(*perceptual_hash));
1805 if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1806 return((ChannelPerceptualHash *) NULL);
1807 artifact=GetImageArtifact(image,"phash:colorspaces");
1808 if (artifact != NULL)
1809 colorspaces=AcquireString(artifact);
1811 colorspaces=AcquireString("sRGB,HCLp");
1812 perceptual_hash[0].number_colorspaces=0;
1813 perceptual_hash[0].number_channels=0;
1815 for (i=0; (p=StringToken(",",&q)) != (char *) NULL; i++)
1831 if (i >= MaximumNumberOfPerceptualColorspaces)
1833 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,p);
1836 perceptual_hash[0].colorspace[i]=(ColorspaceType) colorspace;
1837 hash_image=BlurImage(image,0.0,1.0,exception);
1838 if (hash_image == (Image *) NULL)
1840 hash_image->depth=8;
1841 status=TransformImageColorspace(hash_image,(ColorspaceType) colorspace,
1843 if (status == MagickFalse)
1845 moments=GetImageMoments(hash_image,exception);
1846 perceptual_hash[0].number_colorspaces++;
1847 perceptual_hash[0].number_channels+=GetImageChannels(hash_image);
1848 hash_image=DestroyImage(hash_image);
1849 if (moments == (ChannelMoments *) NULL)
1851 for (channel=0; channel <= MaxPixelChannels; channel++)
1852 for (j=0; j < MaximumNumberOfImageMoments; j++)
1853 perceptual_hash[channel].phash[i][j]=
1854 (-MagickLog10(moments[channel].invariant[j]));
1855 moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1857 colorspaces=DestroyString(colorspaces);
1858 return(perceptual_hash);
1862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1866 % G e t I m a g e R a n g e %
1870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1872 % GetImageRange() returns the range of one or more image channels.
1874 % The format of the GetImageRange method is:
1876 % MagickBooleanType GetImageRange(const Image *image,double *minima,
1877 % double *maxima,ExceptionInfo *exception)
1879 % A description of each parameter follows:
1881 % o image: the image.
1883 % o minima: the minimum value in the channel.
1885 % o maxima: the maximum value in the channel.
1887 % o exception: return any errors or warnings in this structure.
1890 MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1891 double *maxima,ExceptionInfo *exception)
1903 assert(image != (Image *) NULL);
1904 assert(image->signature == MagickCoreSignature);
1905 if (image->debug != MagickFalse)
1906 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1908 initialize=MagickTrue;
1911 image_view=AcquireVirtualCacheView(image,exception);
1912 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1913 #pragma omp parallel for schedule(static,4) shared(status,initialize) \
1914 magick_threads(image,image,image->rows,1)
1916 for (y=0; y < (ssize_t) image->rows; y++)
1925 register const Quantum
1931 if (status == MagickFalse)
1933 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1934 if (p == (const Quantum *) NULL)
1939 row_initialize=MagickTrue;
1940 for (x=0; x < (ssize_t) image->columns; x++)
1945 if (GetPixelWriteMask(image,p) == 0)
1947 p+=GetPixelChannels(image);
1950 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1952 PixelChannel channel=GetPixelChannelChannel(image,i);
1953 PixelTrait traits=GetPixelChannelTraits(image,channel);
1954 if (traits == UndefinedPixelTrait)
1956 if ((traits & UpdatePixelTrait) == 0)
1958 if (row_initialize != MagickFalse)
1960 row_minima=(double) p[i];
1961 row_maxima=(double) p[i];
1962 row_initialize=MagickFalse;
1966 if ((double) p[i] < row_minima)
1967 row_minima=(double) p[i];
1968 if ((double) p[i] > row_maxima)
1969 row_maxima=(double) p[i];
1972 p+=GetPixelChannels(image);
1974 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1975 #pragma omp critical (MagickCore_GetImageRange)
1978 if (initialize != MagickFalse)
1982 initialize=MagickFalse;
1986 if (row_minima < *minima)
1988 if (row_maxima > *maxima)
1993 image_view=DestroyCacheView(image_view);
1998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2002 % G e t I m a g e S t a t i s t i c s %
2006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008 % GetImageStatistics() returns statistics for each channel in the image. The
2009 % statistics include the channel depth, its minima, maxima, mean, standard
2010 % deviation, kurtosis and skewness. You can access the red channel mean, for
2011 % example, like this:
2013 % channel_statistics=GetImageStatistics(image,exception);
2014 % red_mean=channel_statistics[RedPixelChannel].mean;
2016 % Use MagickRelinquishMemory() to free the statistics buffer.
2018 % The format of the GetImageStatistics method is:
2020 % ChannelStatistics *GetImageStatistics(const Image *image,
2021 % ExceptionInfo *exception)
2023 % A description of each parameter follows:
2025 % o image: the image.
2027 % o exception: return any errors or warnings in this structure.
2030 MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
2031 ExceptionInfo *exception)
2034 *channel_statistics;
2055 assert(image != (Image *) NULL);
2056 assert(image->signature == MagickCoreSignature);
2057 if (image->debug != MagickFalse)
2058 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2059 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2060 sizeof(*histogram));
2061 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
2062 MaxPixelChannels+1,sizeof(*channel_statistics));
2063 if ((channel_statistics == (ChannelStatistics *) NULL) ||
2064 (histogram == (double *) NULL))
2066 if (histogram != (double *) NULL)
2067 histogram=(double *) RelinquishMagickMemory(histogram);
2068 if (channel_statistics != (ChannelStatistics *) NULL)
2069 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2070 channel_statistics);
2071 return(channel_statistics);
2073 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
2074 sizeof(*channel_statistics));
2075 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2077 channel_statistics[i].depth=1;
2078 channel_statistics[i].maxima=(-MagickMaximumValue);
2079 channel_statistics[i].minima=MagickMaximumValue;
2081 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*MaxPixelChannels*
2082 sizeof(*histogram));
2083 for (y=0; y < (ssize_t) image->rows; y++)
2085 register const Quantum
2091 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2092 if (p == (const Quantum *) NULL)
2094 for (x=0; x < (ssize_t) image->columns; x++)
2099 if (GetPixelWriteMask(image,p) == 0)
2101 p+=GetPixelChannels(image);
2104 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2106 PixelChannel channel=GetPixelChannelChannel(image,i);
2107 PixelTrait traits=GetPixelChannelTraits(image,channel);
2108 if (traits == UndefinedPixelTrait)
2110 if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
2112 depth=channel_statistics[channel].depth;
2113 range=GetQuantumRange(depth);
2114 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2115 range) ? MagickTrue : MagickFalse;
2116 if (status != MagickFalse)
2118 channel_statistics[channel].depth++;
2123 if ((double) p[i] < channel_statistics[channel].minima)
2124 channel_statistics[channel].minima=(double) p[i];
2125 if ((double) p[i] > channel_statistics[channel].maxima)
2126 channel_statistics[channel].maxima=(double) p[i];
2127 channel_statistics[channel].sum+=p[i];
2128 channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
2129 channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
2130 channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
2132 channel_statistics[channel].area++;
2133 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2134 ClampToQuantum((double) p[i]))+i]++;
2136 p+=GetPixelChannels(image);
2139 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2148 area=PerceptibleReciprocal(channel_statistics[i].area);
2149 channel_statistics[i].sum*=area;
2150 channel_statistics[i].sum_squared*=area;
2151 channel_statistics[i].sum_cubed*=area;
2152 channel_statistics[i].sum_fourth_power*=area;
2153 channel_statistics[i].mean=channel_statistics[i].sum;
2154 channel_statistics[i].variance=channel_statistics[i].sum_squared;
2155 channel_statistics[i].standard_deviation=sqrt(
2156 channel_statistics[i].variance-(channel_statistics[i].mean*
2157 channel_statistics[i].mean));
2159 for (j=0; j < (ssize_t) (MaxMap+1U); j++)
2160 if (histogram[GetPixelChannels(image)*j+i] > 0.0)
2162 for (j=0; j < (ssize_t) (MaxMap+1U); j++)
2167 count=histogram[GetPixelChannels(image)*j+i]*area;
2168 if (number_bins > MagickEpsilon)
2169 channel_statistics[i].entropy+=-count*MagickLog10(count)/
2170 MagickLog10(number_bins);
2173 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2175 PixelTrait traits=GetPixelChannelTraits(image,(PixelChannel) i);
2176 if ((traits & UpdatePixelTrait) == 0)
2178 channel_statistics[CompositePixelChannel].area+=channel_statistics[i].area;
2179 channel_statistics[CompositePixelChannel].minima=MagickMin(
2180 channel_statistics[CompositePixelChannel].minima,
2181 channel_statistics[i].minima);
2182 channel_statistics[CompositePixelChannel].maxima=EvaluateMax(
2183 channel_statistics[CompositePixelChannel].maxima,
2184 channel_statistics[i].maxima);
2185 channel_statistics[CompositePixelChannel].sum+=channel_statistics[i].sum;
2186 channel_statistics[CompositePixelChannel].sum_squared+=
2187 channel_statistics[i].sum_squared;
2188 channel_statistics[CompositePixelChannel].sum_cubed+=
2189 channel_statistics[i].sum_cubed;
2190 channel_statistics[CompositePixelChannel].sum_fourth_power+=
2191 channel_statistics[i].sum_fourth_power;
2192 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
2193 channel_statistics[CompositePixelChannel].variance+=
2194 channel_statistics[i].variance-channel_statistics[i].mean*
2195 channel_statistics[i].mean;
2196 channel_statistics[CompositePixelChannel].standard_deviation+=
2197 channel_statistics[i].variance-channel_statistics[i].mean*
2198 channel_statistics[i].mean;
2199 if (channel_statistics[i].entropy > MagickEpsilon)
2200 channel_statistics[CompositePixelChannel].entropy+=
2201 channel_statistics[i].entropy;
2203 channels=GetImageChannels(image);
2204 channel_statistics[CompositePixelChannel].area/=channels;
2205 channel_statistics[CompositePixelChannel].sum/=channels;
2206 channel_statistics[CompositePixelChannel].sum_squared/=channels;
2207 channel_statistics[CompositePixelChannel].sum_cubed/=channels;
2208 channel_statistics[CompositePixelChannel].sum_fourth_power/=channels;
2209 channel_statistics[CompositePixelChannel].mean/=channels;
2210 channel_statistics[CompositePixelChannel].variance/=channels;
2211 channel_statistics[CompositePixelChannel].standard_deviation=
2212 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/channels);
2213 channel_statistics[CompositePixelChannel].kurtosis/=channels;
2214 channel_statistics[CompositePixelChannel].skewness/=channels;
2215 channel_statistics[CompositePixelChannel].entropy/=channels;
2216 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2221 if (channel_statistics[i].standard_deviation == 0.0)
2223 standard_deviation=PerceptibleReciprocal(
2224 channel_statistics[i].standard_deviation);
2225 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
2226 channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
2227 channel_statistics[i].mean*channel_statistics[i].mean*
2228 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2229 standard_deviation);
2230 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
2231 channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
2232 channel_statistics[i].mean*channel_statistics[i].mean*
2233 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
2234 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
2235 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2236 standard_deviation*standard_deviation)-3.0;
2238 histogram=(double *) RelinquishMagickMemory(histogram);
2239 if (y < (ssize_t) image->rows)
2240 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2241 channel_statistics);
2242 return(channel_statistics);
2246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2250 % P o l y n o m i a l I m a g e %
2254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2256 % PolynomialImage() returns a new image where each pixel is the sum of the
2257 % pixels in the image sequence after applying its corresponding terms
2258 % (coefficient and degree pairs).
2260 % The format of the PolynomialImage method is:
2262 % Image *PolynomialImage(const Image *images,const size_t number_terms,
2263 % const double *terms,ExceptionInfo *exception)
2265 % A description of each parameter follows:
2267 % o images: the image sequence.
2269 % o number_terms: the number of terms in the list. The actual list length
2270 % is 2 x number_terms + 1 (the constant).
2272 % o terms: the list of polynomial coefficients and degree pairs and a
2275 % o exception: return any errors or warnings in this structure.
2279 MagickExport Image *PolynomialImage(const Image *images,
2280 const size_t number_terms,const double *terms,ExceptionInfo *exception)
2282 #define PolynomialImageTag "Polynomial/Image"
2297 **magick_restrict polynomial_pixels;
2305 assert(images != (Image *) NULL);
2306 assert(images->signature == MagickCoreSignature);
2307 if (images->debug != MagickFalse)
2308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2309 assert(exception != (ExceptionInfo *) NULL);
2310 assert(exception->signature == MagickCoreSignature);
2311 image=CloneImage(images,images->columns,images->rows,MagickTrue,
2313 if (image == (Image *) NULL)
2314 return((Image *) NULL);
2315 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2317 image=DestroyImage(image);
2318 return((Image *) NULL);
2320 number_images=GetImageListLength(images);
2321 polynomial_pixels=AcquirePixelThreadSet(images);
2322 if (polynomial_pixels == (PixelChannels **) NULL)
2324 image=DestroyImage(image);
2325 (void) ThrowMagickException(exception,GetMagickModule(),
2326 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2327 return((Image *) NULL);
2330 Polynomial image pixels.
2334 polynomial_view=AcquireAuthenticCacheView(image,exception);
2335 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2336 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2337 magick_threads(image,image,image->rows,1)
2339 for (y=0; y < (ssize_t) image->rows; y++)
2348 id = GetOpenMPThreadId();
2354 register PixelChannels
2363 if (status == MagickFalse)
2365 q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2367 if (q == (Quantum *) NULL)
2372 polynomial_pixel=polynomial_pixels[id];
2373 for (j=0; j < (ssize_t) image->columns; j++)
2374 for (i=0; i < MaxPixelChannels; i++)
2375 polynomial_pixel[j].channel[i]=0.0;
2377 for (j=0; j < (ssize_t) number_images; j++)
2379 register const Quantum
2382 if (j >= (ssize_t) number_terms)
2384 image_view=AcquireVirtualCacheView(next,exception);
2385 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2386 if (p == (const Quantum *) NULL)
2388 image_view=DestroyCacheView(image_view);
2391 for (x=0; x < (ssize_t) image->columns; x++)
2396 if (GetPixelWriteMask(next,p) == 0)
2398 p+=GetPixelChannels(next);
2401 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2407 PixelChannel channel=GetPixelChannelChannel(image,i);
2408 PixelTrait traits=GetPixelChannelTraits(next,channel);
2409 PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
2410 if ((traits == UndefinedPixelTrait) ||
2411 (polynomial_traits == UndefinedPixelTrait))
2413 if ((traits & UpdatePixelTrait) == 0)
2415 coefficient=(MagickRealType) terms[2*j];
2416 degree=(MagickRealType) terms[(j << 1)+1];
2417 polynomial_pixel[x].channel[i]+=coefficient*
2418 pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
2420 p+=GetPixelChannels(next);
2422 image_view=DestroyCacheView(image_view);
2423 next=GetNextImageInList(next);
2425 for (x=0; x < (ssize_t) image->columns; x++)
2430 if (GetPixelWriteMask(image,q) == 0)
2432 q+=GetPixelChannels(image);
2435 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2437 PixelChannel channel=GetPixelChannelChannel(image,i);
2438 PixelTrait traits=GetPixelChannelTraits(image,channel);
2439 if (traits == UndefinedPixelTrait)
2441 if ((traits & UpdatePixelTrait) == 0)
2443 q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
2445 q+=GetPixelChannels(image);
2447 if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2449 if (images->progress_monitor != (MagickProgressMonitor) NULL)
2454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2455 #pragma omp critical (MagickCore_PolynomialImages)
2457 proceed=SetImageProgress(images,PolynomialImageTag,progress++,
2459 if (proceed == MagickFalse)
2463 polynomial_view=DestroyCacheView(polynomial_view);
2464 polynomial_pixels=DestroyPixelThreadSet(polynomial_pixels);
2465 if (status == MagickFalse)
2466 image=DestroyImage(image);
2471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2475 % S t a t i s t i c I m a g e %
2479 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2481 % StatisticImage() makes each pixel the min / max / median / mode / etc. of
2482 % the neighborhood of the specified width and height.
2484 % The format of the StatisticImage method is:
2486 % Image *StatisticImage(const Image *image,const StatisticType type,
2487 % const size_t width,const size_t height,ExceptionInfo *exception)
2489 % A description of each parameter follows:
2491 % o image: the image.
2493 % o type: the statistic type (median, mode, etc.).
2495 % o width: the width of the pixel neighborhood.
2497 % o height: the height of the pixel neighborhood.
2499 % o exception: return any errors or warnings in this structure.
2503 typedef struct _SkipNode
2511 typedef struct _SkipList
2520 typedef struct _PixelList
2533 static PixelList *DestroyPixelList(PixelList *pixel_list)
2535 if (pixel_list == (PixelList *) NULL)
2536 return((PixelList *) NULL);
2537 if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2538 pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
2539 pixel_list->skip_list.nodes);
2540 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2544 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2549 assert(pixel_list != (PixelList **) NULL);
2550 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2551 if (pixel_list[i] != (PixelList *) NULL)
2552 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2553 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2557 static PixelList *AcquirePixelList(const size_t width,const size_t height)
2562 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2563 if (pixel_list == (PixelList *) NULL)
2565 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2566 pixel_list->length=width*height;
2567 pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
2568 sizeof(*pixel_list->skip_list.nodes));
2569 if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2570 return(DestroyPixelList(pixel_list));
2571 (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
2572 sizeof(*pixel_list->skip_list.nodes));
2573 pixel_list->signature=MagickCoreSignature;
2577 static PixelList **AcquirePixelListThreadSet(const size_t width,
2578 const size_t height)
2589 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2590 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2591 sizeof(*pixel_list));
2592 if (pixel_list == (PixelList **) NULL)
2593 return((PixelList **) NULL);
2594 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2595 for (i=0; i < (ssize_t) number_threads; i++)
2597 pixel_list[i]=AcquirePixelList(width,height);
2598 if (pixel_list[i] == (PixelList *) NULL)
2599 return(DestroyPixelListThreadSet(pixel_list));
2604 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
2617 Initialize the node.
2619 p=(&pixel_list->skip_list);
2620 p->nodes[color].signature=pixel_list->signature;
2621 p->nodes[color].count=1;
2623 Determine where it belongs in the list.
2626 for (level=p->level; level >= 0; level--)
2628 while (p->nodes[search].next[level] < color)
2629 search=p->nodes[search].next[level];
2630 update[level]=search;
2633 Generate a pseudo-random level for this node.
2635 for (level=0; ; level++)
2637 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2638 if ((pixel_list->seed & 0x300) != 0x300)
2643 if (level > (p->level+2))
2646 If we're raising the list's level, link back to the root node.
2648 while (level > p->level)
2651 update[p->level]=65536UL;
2654 Link the node into the skip-list.
2658 p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2659 p->nodes[update[level]].next[level]=color;
2660 } while (level-- > 0);
2663 static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
2676 Find the maximum value for each of the color.
2678 p=(&pixel_list->skip_list);
2681 maximum=p->nodes[color].next[0];
2684 color=p->nodes[color].next[0];
2685 if (color > maximum)
2687 count+=p->nodes[color].count;
2688 } while (count < (ssize_t) pixel_list->length);
2689 *pixel=ScaleShortToQuantum((unsigned short) maximum);
2692 static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
2707 Find the mean value for each of the color.
2709 p=(&pixel_list->skip_list);
2715 color=p->nodes[color].next[0];
2716 sum+=(double) p->nodes[color].count*color;
2717 count+=p->nodes[color].count;
2718 } while (count < (ssize_t) pixel_list->length);
2719 sum/=pixel_list->length;
2720 *pixel=ScaleShortToQuantum((unsigned short) sum);
2723 static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2735 Find the median value for each of the color.
2737 p=(&pixel_list->skip_list);
2742 color=p->nodes[color].next[0];
2743 count+=p->nodes[color].count;
2744 } while (count <= (ssize_t) (pixel_list->length >> 1));
2745 *pixel=ScaleShortToQuantum((unsigned short) color);
2748 static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
2761 Find the minimum value for each of the color.
2763 p=(&pixel_list->skip_list);
2766 minimum=p->nodes[color].next[0];
2769 color=p->nodes[color].next[0];
2770 if (color < minimum)
2772 count+=p->nodes[color].count;
2773 } while (count < (ssize_t) pixel_list->length);
2774 *pixel=ScaleShortToQuantum((unsigned short) minimum);
2777 static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2791 Make each pixel the 'predominant color' of the specified neighborhood.
2793 p=(&pixel_list->skip_list);
2796 max_count=p->nodes[mode].count;
2800 color=p->nodes[color].next[0];
2801 if (p->nodes[color].count > max_count)
2804 max_count=p->nodes[mode].count;
2806 count+=p->nodes[color].count;
2807 } while (count < (ssize_t) pixel_list->length);
2808 *pixel=ScaleShortToQuantum((unsigned short) mode);
2811 static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2825 Finds the non peak value for each of the colors.
2827 p=(&pixel_list->skip_list);
2829 next=p->nodes[color].next[0];
2835 next=p->nodes[color].next[0];
2836 count+=p->nodes[color].count;
2837 } while (count <= (ssize_t) (pixel_list->length >> 1));
2838 if ((previous == 65536UL) && (next != 65536UL))
2841 if ((previous != 65536UL) && (next == 65536UL))
2843 *pixel=ScaleShortToQuantum((unsigned short) color);
2846 static inline void GetRootMeanSquarePixelList(PixelList *pixel_list,
2862 Find the root mean square value for each of the color.
2864 p=(&pixel_list->skip_list);
2870 color=p->nodes[color].next[0];
2871 sum+=(double) (p->nodes[color].count*color*color);
2872 count+=p->nodes[color].count;
2873 } while (count < (ssize_t) pixel_list->length);
2874 sum/=pixel_list->length;
2875 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum));
2878 static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2895 Find the standard-deviation value for each of the color.
2897 p=(&pixel_list->skip_list);
2907 color=p->nodes[color].next[0];
2908 sum+=(double) p->nodes[color].count*color;
2909 for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2910 sum_squared+=((double) color)*((double) color);
2911 count+=p->nodes[color].count;
2912 } while (count < (ssize_t) pixel_list->length);
2913 sum/=pixel_list->length;
2914 sum_squared/=pixel_list->length;
2915 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2918 static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
2926 index=ScaleQuantumToShort(pixel);
2927 signature=pixel_list->skip_list.nodes[index].signature;
2928 if (signature == pixel_list->signature)
2930 pixel_list->skip_list.nodes[index].count++;
2933 AddNodePixelList(pixel_list,index);
2936 static void ResetPixelList(PixelList *pixel_list)
2948 Reset the skip-list.
2950 p=(&pixel_list->skip_list);
2951 root=p->nodes+65536UL;
2953 for (level=0; level < 9; level++)
2954 root->next[level]=65536UL;
2955 pixel_list->seed=pixel_list->signature++;
2958 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2959 const size_t width,const size_t height,ExceptionInfo *exception)
2961 #define StatisticImageTag "Statistic/Image"
2977 **magick_restrict pixel_list;
2984 Initialize statistics image attributes.
2986 assert(image != (Image *) NULL);
2987 assert(image->signature == MagickCoreSignature);
2988 if (image->debug != MagickFalse)
2989 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2990 assert(exception != (ExceptionInfo *) NULL);
2991 assert(exception->signature == MagickCoreSignature);
2992 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2994 if (statistic_image == (Image *) NULL)
2995 return((Image *) NULL);
2996 status=SetImageStorageClass(statistic_image,DirectClass,exception);
2997 if (status == MagickFalse)
2999 statistic_image=DestroyImage(statistic_image);
3000 return((Image *) NULL);
3002 pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
3003 if (pixel_list == (PixelList **) NULL)
3005 statistic_image=DestroyImage(statistic_image);
3006 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3009 Make each pixel the min / max / median / mode / etc. of the neighborhood.
3011 center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
3012 (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
3015 image_view=AcquireVirtualCacheView(image,exception);
3016 statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
3017 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3018 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3019 magick_threads(image,statistic_image,statistic_image->rows,1)
3021 for (y=0; y < (ssize_t) statistic_image->rows; y++)
3024 id = GetOpenMPThreadId();
3026 register const Quantum
3035 if (status == MagickFalse)
3037 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
3038 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
3039 MagickMax(height,1),exception);
3040 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
3041 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3046 for (x=0; x < (ssize_t) statistic_image->columns; x++)
3051 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3056 register const Quantum
3057 *magick_restrict pixels;
3065 PixelChannel channel=GetPixelChannelChannel(image,i);
3066 PixelTrait traits=GetPixelChannelTraits(image,channel);
3067 PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
3069 if ((traits == UndefinedPixelTrait) ||
3070 (statistic_traits == UndefinedPixelTrait))
3072 if (((statistic_traits & CopyPixelTrait) != 0) ||
3073 (GetPixelWriteMask(image,p) == 0))
3075 SetPixelChannel(statistic_image,channel,p[center+i],q);
3079 ResetPixelList(pixel_list[id]);
3080 for (v=0; v < (ssize_t) MagickMax(height,1); v++)
3082 for (u=0; u < (ssize_t) MagickMax(width,1); u++)
3084 InsertPixelList(pixels[i],pixel_list[id]);
3085 pixels+=GetPixelChannels(image);
3087 pixels+=GetPixelChannels(image)*image->columns;
3091 case GradientStatistic:
3097 GetMinimumPixelList(pixel_list[id],&pixel);
3098 minimum=(double) pixel;
3099 GetMaximumPixelList(pixel_list[id],&pixel);
3100 maximum=(double) pixel;
3101 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
3104 case MaximumStatistic:
3106 GetMaximumPixelList(pixel_list[id],&pixel);
3111 GetMeanPixelList(pixel_list[id],&pixel);
3114 case MedianStatistic:
3117 GetMedianPixelList(pixel_list[id],&pixel);
3120 case MinimumStatistic:
3122 GetMinimumPixelList(pixel_list[id],&pixel);
3127 GetModePixelList(pixel_list[id],&pixel);
3130 case NonpeakStatistic:
3132 GetNonpeakPixelList(pixel_list[id],&pixel);
3135 case RootMeanSquareStatistic:
3137 GetRootMeanSquarePixelList(pixel_list[id],&pixel);
3140 case StandardDeviationStatistic:
3142 GetStandardDeviationPixelList(pixel_list[id],&pixel);
3146 SetPixelChannel(statistic_image,channel,pixel,q);
3148 p+=GetPixelChannels(image);
3149 q+=GetPixelChannels(statistic_image);
3151 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3153 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3158 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3159 #pragma omp critical (MagickCore_StatisticImage)
3161 proceed=SetImageProgress(image,StatisticImageTag,progress++,
3163 if (proceed == MagickFalse)
3167 statistic_view=DestroyCacheView(statistic_view);
3168 image_view=DestroyCacheView(image_view);
3169 pixel_list=DestroyPixelListThreadSet(pixel_list);
3170 if (status == MagickFalse)
3171 statistic_image=DestroyImage(statistic_image);
3172 return(statistic_image);