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 % https://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 ((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 if ((traits & UpdatePixelTrait) == 0)
834 result=ApplyEvaluateOperator(random_info[id],q[i],op,value);
835 if (op == MeanEvaluateOperator)
837 q[i]=ClampToQuantum(result);
839 q+=GetPixelChannels(image);
841 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
843 if (image->progress_monitor != (MagickProgressMonitor) NULL)
848 #if defined(MAGICKCORE_OPENMP_SUPPORT)
849 #pragma omp critical (MagickCore_EvaluateImage)
851 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
852 if (proceed == MagickFalse)
856 image_view=DestroyCacheView(image_view);
857 random_info=DestroyRandomInfoThreadSet(random_info);
862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866 % F u n c t i o n I m a g e %
870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 % FunctionImage() applies a value to the image with an arithmetic, relational,
873 % or logical operator to an image. Use these operations to lighten or darken
874 % an image, to increase or decrease contrast in an image, or to produce the
875 % "negative" of an image.
877 % The format of the FunctionImage method is:
879 % MagickBooleanType FunctionImage(Image *image,
880 % const MagickFunction function,const ssize_t number_parameters,
881 % const double *parameters,ExceptionInfo *exception)
883 % A description of each parameter follows:
885 % o image: the image.
887 % o function: A channel function.
889 % o parameters: one or more parameters.
891 % o exception: return any errors or warnings in this structure.
895 static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
896 const size_t number_parameters,const double *parameters,
897 ExceptionInfo *exception)
909 case PolynomialFunction:
912 Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+
916 for (i=0; i < (ssize_t) number_parameters; i++)
917 result=result*QuantumScale*pixel+parameters[i];
918 result*=QuantumRange;
921 case SinusoidFunction:
930 Sinusoid: frequency, phase, amplitude, bias.
932 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
933 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
934 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
935 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
936 result=(double) (QuantumRange*(amplitude*sin((double) (2.0*
937 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
949 Arcsin (peged at range limits for invalid results): width, center,
952 width=(number_parameters >= 1) ? parameters[0] : 1.0;
953 center=(number_parameters >= 2) ? parameters[1] : 0.5;
954 range=(number_parameters >= 3) ? parameters[2] : 1.0;
955 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
956 result=2.0/width*(QuantumScale*pixel-center);
957 if ( result <= -1.0 )
958 result=bias-range/2.0;
961 result=bias+range/2.0;
963 result=(double) (range/MagickPI*asin((double) result)+bias);
964 result*=QuantumRange;
976 Arctan: slope, center, range, and bias.
978 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
979 center=(number_parameters >= 2) ? parameters[1] : 0.5;
980 range=(number_parameters >= 3) ? parameters[2] : 1.0;
981 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
982 result=(double) (MagickPI*slope*(QuantumScale*pixel-center));
983 result=(double) (QuantumRange*(range/MagickPI*atan((double)
987 case UndefinedFunction:
990 return(ClampToQuantum(result));
993 MagickExport MagickBooleanType FunctionImage(Image *image,
994 const MagickFunction function,const size_t number_parameters,
995 const double *parameters,ExceptionInfo *exception)
997 #define FunctionImageTag "Function/Image "
1011 assert(image != (Image *) NULL);
1012 assert(image->signature == MagickCoreSignature);
1013 if (image->debug != MagickFalse)
1014 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1015 assert(exception != (ExceptionInfo *) NULL);
1016 assert(exception->signature == MagickCoreSignature);
1017 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1018 if (AccelerateFunctionImage(image,function,number_parameters,parameters,
1019 exception) != MagickFalse)
1022 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1023 return(MagickFalse);
1026 image_view=AcquireAuthenticCacheView(image,exception);
1027 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1028 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1029 magick_threads(image,image,image->rows,1)
1031 for (y=0; y < (ssize_t) image->rows; y++)
1039 if (status == MagickFalse)
1041 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1042 if (q == (Quantum *) NULL)
1047 for (x=0; x < (ssize_t) image->columns; x++)
1052 if (GetPixelWriteMask(image,q) == 0)
1054 q+=GetPixelChannels(image);
1057 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1059 PixelChannel channel=GetPixelChannelChannel(image,i);
1060 PixelTrait traits=GetPixelChannelTraits(image,channel);
1061 if (traits == UndefinedPixelTrait)
1063 if ((traits & UpdatePixelTrait) == 0)
1065 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1068 q+=GetPixelChannels(image);
1070 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1072 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1078 #pragma omp critical (MagickCore_FunctionImage)
1080 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1081 if (proceed == MagickFalse)
1085 image_view=DestroyCacheView(image_view);
1090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094 % G e t I m a g e E n t r o p y %
1098 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100 % GetImageEntropy() returns the entropy of one or more image channels.
1102 % The format of the GetImageEntropy method is:
1104 % MagickBooleanType GetImageEntropy(const Image *image,double *entropy,
1105 % ExceptionInfo *exception)
1107 % A description of each parameter follows:
1109 % o image: the image.
1111 % o entropy: the average entropy of the selected channels.
1113 % o exception: return any errors or warnings in this structure.
1116 MagickExport MagickBooleanType GetImageEntropy(const Image *image,
1117 double *entropy,ExceptionInfo *exception)
1120 *channel_statistics;
1122 assert(image != (Image *) NULL);
1123 assert(image->signature == MagickCoreSignature);
1124 if (image->debug != MagickFalse)
1125 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1126 channel_statistics=GetImageStatistics(image,exception);
1127 if (channel_statistics == (ChannelStatistics *) NULL)
1128 return(MagickFalse);
1129 *entropy=channel_statistics[CompositePixelChannel].entropy;
1130 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1131 channel_statistics);
1136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140 % G e t I m a g e E x t r e m a %
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 % GetImageExtrema() returns the extrema of one or more image channels.
1148 % The format of the GetImageExtrema method is:
1150 % MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1151 % size_t *maxima,ExceptionInfo *exception)
1153 % A description of each parameter follows:
1155 % o image: the image.
1157 % o minima: the minimum value in the channel.
1159 % o maxima: the maximum value in the channel.
1161 % o exception: return any errors or warnings in this structure.
1164 MagickExport MagickBooleanType GetImageExtrema(const Image *image,
1165 size_t *minima,size_t *maxima,ExceptionInfo *exception)
1174 assert(image != (Image *) NULL);
1175 assert(image->signature == MagickCoreSignature);
1176 if (image->debug != MagickFalse)
1177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1178 status=GetImageRange(image,&min,&max,exception);
1179 *minima=(size_t) ceil(min-0.5);
1180 *maxima=(size_t) floor(max+0.5);
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1189 % G e t I m a g e K u r t o s i s %
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1195 % GetImageKurtosis() returns the kurtosis and skewness of one or more image
1198 % The format of the GetImageKurtosis method is:
1200 % MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1201 % double *skewness,ExceptionInfo *exception)
1203 % A description of each parameter follows:
1205 % o image: the image.
1207 % o kurtosis: the kurtosis of the channel.
1209 % o skewness: the skewness of the channel.
1211 % o exception: return any errors or warnings in this structure.
1214 MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1215 double *kurtosis,double *skewness,ExceptionInfo *exception)
1218 *channel_statistics;
1220 assert(image != (Image *) NULL);
1221 assert(image->signature == MagickCoreSignature);
1222 if (image->debug != MagickFalse)
1223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1224 channel_statistics=GetImageStatistics(image,exception);
1225 if (channel_statistics == (ChannelStatistics *) NULL)
1226 return(MagickFalse);
1227 *kurtosis=channel_statistics[CompositePixelChannel].kurtosis;
1228 *skewness=channel_statistics[CompositePixelChannel].skewness;
1229 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1230 channel_statistics);
1235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239 % G e t I m a g e M e a n %
1243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 % GetImageMean() returns the mean and standard deviation of one or more image
1248 % The format of the GetImageMean method is:
1250 % MagickBooleanType GetImageMean(const Image *image,double *mean,
1251 % double *standard_deviation,ExceptionInfo *exception)
1253 % A description of each parameter follows:
1255 % o image: the image.
1257 % o mean: the average value in the channel.
1259 % o standard_deviation: the standard deviation of the channel.
1261 % o exception: return any errors or warnings in this structure.
1264 MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1265 double *standard_deviation,ExceptionInfo *exception)
1268 *channel_statistics;
1270 assert(image != (Image *) NULL);
1271 assert(image->signature == MagickCoreSignature);
1272 if (image->debug != MagickFalse)
1273 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1274 channel_statistics=GetImageStatistics(image,exception);
1275 if (channel_statistics == (ChannelStatistics *) NULL)
1276 return(MagickFalse);
1277 *mean=channel_statistics[CompositePixelChannel].mean;
1278 *standard_deviation=
1279 channel_statistics[CompositePixelChannel].standard_deviation;
1280 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1281 channel_statistics);
1286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290 % G e t I m a g e M o m e n t s %
1294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296 % GetImageMoments() returns the normalized moments of one or more image
1299 % The format of the GetImageMoments method is:
1301 % ChannelMoments *GetImageMoments(const Image *image,
1302 % ExceptionInfo *exception)
1304 % A description of each parameter follows:
1306 % o image: the image.
1308 % o exception: return any errors or warnings in this structure.
1312 static size_t GetImageChannels(const Image *image)
1321 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1323 PixelChannel channel=GetPixelChannelChannel(image,i);
1324 PixelTrait traits=GetPixelChannelTraits(image,channel);
1325 if (traits == UndefinedPixelTrait)
1329 return((size_t) (channels == 0 ? 1 : channels));
1332 MagickExport ChannelMoments *GetImageMoments(const Image *image,
1333 ExceptionInfo *exception)
1335 #define MaxNumberImageMoments 8
1344 M00[MaxPixelChannels+1],
1345 M01[MaxPixelChannels+1],
1346 M02[MaxPixelChannels+1],
1347 M03[MaxPixelChannels+1],
1348 M10[MaxPixelChannels+1],
1349 M11[MaxPixelChannels+1],
1350 M12[MaxPixelChannels+1],
1351 M20[MaxPixelChannels+1],
1352 M21[MaxPixelChannels+1],
1353 M22[MaxPixelChannels+1],
1354 M30[MaxPixelChannels+1];
1357 centroid[MaxPixelChannels+1];
1363 assert(image != (Image *) NULL);
1364 assert(image->signature == MagickCoreSignature);
1365 if (image->debug != MagickFalse)
1366 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1367 channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1368 sizeof(*channel_moments));
1369 if (channel_moments == (ChannelMoments *) NULL)
1370 return(channel_moments);
1371 (void) ResetMagickMemory(channel_moments,0,(MaxPixelChannels+1)*
1372 sizeof(*channel_moments));
1373 (void) ResetMagickMemory(centroid,0,sizeof(centroid));
1374 (void) ResetMagickMemory(M00,0,sizeof(M00));
1375 (void) ResetMagickMemory(M01,0,sizeof(M01));
1376 (void) ResetMagickMemory(M02,0,sizeof(M02));
1377 (void) ResetMagickMemory(M03,0,sizeof(M03));
1378 (void) ResetMagickMemory(M10,0,sizeof(M10));
1379 (void) ResetMagickMemory(M11,0,sizeof(M11));
1380 (void) ResetMagickMemory(M12,0,sizeof(M12));
1381 (void) ResetMagickMemory(M20,0,sizeof(M20));
1382 (void) ResetMagickMemory(M21,0,sizeof(M21));
1383 (void) ResetMagickMemory(M22,0,sizeof(M22));
1384 (void) ResetMagickMemory(M30,0,sizeof(M30));
1385 image_view=AcquireVirtualCacheView(image,exception);
1386 for (y=0; y < (ssize_t) image->rows; y++)
1388 register const Quantum
1395 Compute center of mass (centroid).
1397 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1398 if (p == (const Quantum *) NULL)
1400 for (x=0; x < (ssize_t) image->columns; x++)
1405 if (GetPixelWriteMask(image,p) == 0)
1407 p+=GetPixelChannels(image);
1410 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1412 PixelChannel channel=GetPixelChannelChannel(image,i);
1413 PixelTrait traits=GetPixelChannelTraits(image,channel);
1414 if (traits == UndefinedPixelTrait)
1416 if ((traits & UpdatePixelTrait) == 0)
1418 M00[channel]+=QuantumScale*p[i];
1419 M00[MaxPixelChannels]+=QuantumScale*p[i];
1420 M10[channel]+=x*QuantumScale*p[i];
1421 M10[MaxPixelChannels]+=x*QuantumScale*p[i];
1422 M01[channel]+=y*QuantumScale*p[i];
1423 M01[MaxPixelChannels]+=y*QuantumScale*p[i];
1425 p+=GetPixelChannels(image);
1428 for (channel=0; channel <= MaxPixelChannels; channel++)
1431 Compute center of mass (centroid).
1433 if (M00[channel] < MagickEpsilon)
1435 M00[channel]+=MagickEpsilon;
1436 centroid[channel].x=(double) image->columns/2.0;
1437 centroid[channel].y=(double) image->rows/2.0;
1440 M00[channel]+=MagickEpsilon;
1441 centroid[channel].x=M10[channel]/M00[channel];
1442 centroid[channel].y=M01[channel]/M00[channel];
1444 for (y=0; y < (ssize_t) image->rows; y++)
1446 register const Quantum
1453 Compute the image moments.
1455 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1456 if (p == (const Quantum *) NULL)
1458 for (x=0; x < (ssize_t) image->columns; x++)
1463 if (GetPixelWriteMask(image,p) == 0)
1465 p+=GetPixelChannels(image);
1468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1470 PixelChannel channel=GetPixelChannelChannel(image,i);
1471 PixelTrait traits=GetPixelChannelTraits(image,channel);
1472 if (traits == UndefinedPixelTrait)
1474 if ((traits & UpdatePixelTrait) == 0)
1476 M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1478 M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1480 M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1482 M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1484 M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1486 M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1488 M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1489 (y-centroid[channel].y)*QuantumScale*p[i];
1490 M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1491 (y-centroid[channel].y)*QuantumScale*p[i];
1492 M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1493 (y-centroid[channel].y)*QuantumScale*p[i];
1494 M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1495 (y-centroid[channel].y)*QuantumScale*p[i];
1496 M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1497 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1498 M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1499 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1500 M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1501 (x-centroid[channel].x)*QuantumScale*p[i];
1502 M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1503 (x-centroid[channel].x)*QuantumScale*p[i];
1504 M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1505 (y-centroid[channel].y)*QuantumScale*p[i];
1506 M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1507 (y-centroid[channel].y)*QuantumScale*p[i];
1509 p+=GetPixelChannels(image);
1512 M00[MaxPixelChannels]/=GetImageChannels(image);
1513 M01[MaxPixelChannels]/=GetImageChannels(image);
1514 M02[MaxPixelChannels]/=GetImageChannels(image);
1515 M03[MaxPixelChannels]/=GetImageChannels(image);
1516 M10[MaxPixelChannels]/=GetImageChannels(image);
1517 M11[MaxPixelChannels]/=GetImageChannels(image);
1518 M12[MaxPixelChannels]/=GetImageChannels(image);
1519 M20[MaxPixelChannels]/=GetImageChannels(image);
1520 M21[MaxPixelChannels]/=GetImageChannels(image);
1521 M22[MaxPixelChannels]/=GetImageChannels(image);
1522 M30[MaxPixelChannels]/=GetImageChannels(image);
1523 for (channel=0; channel <= MaxPixelChannels; channel++)
1526 Compute elliptical angle, major and minor axes, eccentricity, & intensity.
1528 channel_moments[channel].centroid=centroid[channel];
1529 channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
1530 ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
1531 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1532 channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
1533 ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
1534 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1535 channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
1536 M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
1537 if (fabs(M11[channel]) < MagickEpsilon)
1539 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1540 channel_moments[channel].ellipse_angle+=0.0;
1542 if ((M20[channel]-M02[channel]) < 0.0)
1543 channel_moments[channel].ellipse_angle+=90.0;
1545 channel_moments[channel].ellipse_angle+=0.0;
1548 if (M11[channel] < 0.0)
1550 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1551 channel_moments[channel].ellipse_angle+=0.0;
1553 if ((M20[channel]-M02[channel]) < 0.0)
1554 channel_moments[channel].ellipse_angle+=90.0;
1556 channel_moments[channel].ellipse_angle+=180.0;
1560 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1561 channel_moments[channel].ellipse_angle+=0.0;
1563 if ((M20[channel]-M02[channel]) < 0.0)
1564 channel_moments[channel].ellipse_angle+=90.0;
1566 channel_moments[channel].ellipse_angle+=0.0;
1568 channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
1569 channel_moments[channel].ellipse_axis.y/
1570 (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
1571 channel_moments[channel].ellipse_intensity=M00[channel]/
1572 (MagickPI*channel_moments[channel].ellipse_axis.x*
1573 channel_moments[channel].ellipse_axis.y+MagickEpsilon);
1575 for (channel=0; channel <= MaxPixelChannels; channel++)
1578 Normalize image moments.
1582 M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
1583 M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
1584 M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
1585 M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
1586 M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
1587 M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
1588 M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
1589 M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
1592 image_view=DestroyCacheView(image_view);
1593 for (channel=0; channel <= MaxPixelChannels; channel++)
1596 Compute Hu invariant moments.
1598 channel_moments[channel].invariant[0]=M20[channel]+M02[channel];
1599 channel_moments[channel].invariant[1]=(M20[channel]-M02[channel])*
1600 (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
1601 channel_moments[channel].invariant[2]=(M30[channel]-3.0*M12[channel])*
1602 (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
1603 (3.0*M21[channel]-M03[channel]);
1604 channel_moments[channel].invariant[3]=(M30[channel]+M12[channel])*
1605 (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
1606 (M21[channel]+M03[channel]);
1607 channel_moments[channel].invariant[4]=(M30[channel]-3.0*M12[channel])*
1608 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1609 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1610 (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
1611 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1612 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1613 (M21[channel]+M03[channel]));
1614 channel_moments[channel].invariant[5]=(M20[channel]-M02[channel])*
1615 ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
1616 (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
1617 4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
1618 channel_moments[channel].invariant[6]=(3.0*M21[channel]-M03[channel])*
1619 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1620 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1621 (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
1622 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1623 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1624 (M21[channel]+M03[channel]));
1625 channel_moments[channel].invariant[7]=M11[channel]*((M30[channel]+
1626 M12[channel])*(M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
1627 (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
1628 (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
1630 if (y < (ssize_t) image->rows)
1631 channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1632 return(channel_moments);
1636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1640 % 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 %
1644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646 % GetImagePerceptualHash() returns the perceptual hash of one or more
1649 % The format of the GetImagePerceptualHash method is:
1651 % ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1652 % ExceptionInfo *exception)
1654 % A description of each parameter follows:
1656 % o image: the image.
1658 % o exception: return any errors or warnings in this structure.
1662 static inline double MagickLog10(const double x)
1664 #define Log10Epsilon (1.0e-11)
1666 if (fabs(x) < Log10Epsilon)
1667 return(log10(Log10Epsilon));
1668 return(log10(fabs(x)));
1671 MagickExport ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1672 ExceptionInfo *exception)
1674 ChannelPerceptualHash
1693 perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1694 MaxPixelChannels+1UL,sizeof(*perceptual_hash));
1695 if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1696 return((ChannelPerceptualHash *) NULL);
1697 artifact=GetImageArtifact(image,"phash:colorspaces");
1698 if (artifact != NULL)
1699 colorspaces=AcquireString(artifact);
1701 colorspaces=AcquireString("sRGB,HCLp");
1702 perceptual_hash[0].number_colorspaces=0;
1703 perceptual_hash[0].number_channels=0;
1705 for (i=0; (p=StringToken(",",&q)) != (char *) NULL; i++)
1720 if (i >= MaximumNumberOfPerceptualColorspaces)
1722 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,p);
1725 perceptual_hash[0].colorspace[i]=(ColorspaceType) colorspace;
1726 hash_image=BlurImage(image,0.0,1.0,exception);
1727 if (hash_image == (Image *) NULL)
1729 hash_image->depth=8;
1730 status=TransformImageColorspace(hash_image,(ColorspaceType) colorspace,
1732 if (status == MagickFalse)
1734 moments=GetImageMoments(hash_image,exception);
1735 perceptual_hash[0].number_colorspaces++;
1736 perceptual_hash[0].number_channels+=GetImageChannels(hash_image);
1737 hash_image=DestroyImage(hash_image);
1738 if (moments == (ChannelMoments *) NULL)
1740 for (channel=0; channel <= MaxPixelChannels; channel++)
1741 for (j=0; j < MaximumNumberOfImageMoments; j++)
1742 perceptual_hash[channel].phash[i][j]=
1743 (-MagickLog10(moments[channel].invariant[j]));
1744 moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1746 colorspaces=DestroyString(colorspaces);
1747 return(perceptual_hash);
1751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1755 % G e t I m a g e R a n g e %
1759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1761 % GetImageRange() returns the range of one or more image channels.
1763 % The format of the GetImageRange method is:
1765 % MagickBooleanType GetImageRange(const Image *image,double *minima,
1766 % double *maxima,ExceptionInfo *exception)
1768 % A description of each parameter follows:
1770 % o image: the image.
1772 % o minima: the minimum value in the channel.
1774 % o maxima: the maximum value in the channel.
1776 % o exception: return any errors or warnings in this structure.
1779 MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1780 double *maxima,ExceptionInfo *exception)
1792 assert(image != (Image *) NULL);
1793 assert(image->signature == MagickCoreSignature);
1794 if (image->debug != MagickFalse)
1795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1797 initialize=MagickTrue;
1800 image_view=AcquireVirtualCacheView(image,exception);
1801 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1802 #pragma omp parallel for schedule(static,4) shared(status,initialize) \
1803 magick_threads(image,image,image->rows,1)
1805 for (y=0; y < (ssize_t) image->rows; y++)
1814 register const Quantum
1820 if (status == MagickFalse)
1822 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1823 if (p == (const Quantum *) NULL)
1828 row_initialize=MagickTrue;
1829 for (x=0; x < (ssize_t) image->columns; x++)
1834 if (GetPixelWriteMask(image,p) == 0)
1836 p+=GetPixelChannels(image);
1839 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1841 PixelChannel channel=GetPixelChannelChannel(image,i);
1842 PixelTrait traits=GetPixelChannelTraits(image,channel);
1843 if (traits == UndefinedPixelTrait)
1845 if ((traits & UpdatePixelTrait) == 0)
1847 if (row_initialize != MagickFalse)
1849 row_minima=(double) p[i];
1850 row_maxima=(double) p[i];
1851 row_initialize=MagickFalse;
1855 if ((double) p[i] < row_minima)
1856 row_minima=(double) p[i];
1857 if ((double) p[i] > row_maxima)
1858 row_maxima=(double) p[i];
1861 p+=GetPixelChannels(image);
1863 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1864 #pragma omp critical (MagickCore_GetImageRange)
1867 if (initialize != MagickFalse)
1871 initialize=MagickFalse;
1875 if (row_minima < *minima)
1877 if (row_maxima > *maxima)
1882 image_view=DestroyCacheView(image_view);
1887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1891 % G e t I m a g e S t a t i s t i c s %
1895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1897 % GetImageStatistics() returns statistics for each channel in the image. The
1898 % statistics include the channel depth, its minima, maxima, mean, standard
1899 % deviation, kurtosis and skewness. You can access the red channel mean, for
1900 % example, like this:
1902 % channel_statistics=GetImageStatistics(image,exception);
1903 % red_mean=channel_statistics[RedPixelChannel].mean;
1905 % Use MagickRelinquishMemory() to free the statistics buffer.
1907 % The format of the GetImageStatistics method is:
1909 % ChannelStatistics *GetImageStatistics(const Image *image,
1910 % ExceptionInfo *exception)
1912 % A description of each parameter follows:
1914 % o image: the image.
1916 % o exception: return any errors or warnings in this structure.
1919 MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
1920 ExceptionInfo *exception)
1923 *channel_statistics;
1945 assert(image != (Image *) NULL);
1946 assert(image->signature == MagickCoreSignature);
1947 if (image->debug != MagickFalse)
1948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1949 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1950 sizeof(*histogram));
1951 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1952 MaxPixelChannels+1,sizeof(*channel_statistics));
1953 if ((channel_statistics == (ChannelStatistics *) NULL) ||
1954 (histogram == (double *) NULL))
1956 if (histogram != (double *) NULL)
1957 histogram=(double *) RelinquishMagickMemory(histogram);
1958 if (channel_statistics != (ChannelStatistics *) NULL)
1959 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1960 channel_statistics);
1961 return(channel_statistics);
1963 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
1964 sizeof(*channel_statistics));
1965 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1967 channel_statistics[i].depth=1;
1968 channel_statistics[i].maxima=(-MagickMaximumValue);
1969 channel_statistics[i].minima=MagickMaximumValue;
1971 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1972 sizeof(*histogram));
1973 for (y=0; y < (ssize_t) image->rows; y++)
1975 register const Quantum
1982 Compute pixel statistics.
1984 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1985 if (p == (const Quantum *) NULL)
1987 for (x=0; x < (ssize_t) image->columns; x++)
1992 if (GetPixelWriteMask(image,p) == 0)
1994 p+=GetPixelChannels(image);
1997 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1999 PixelChannel channel=GetPixelChannelChannel(image,i);
2000 PixelTrait traits=GetPixelChannelTraits(image,channel);
2001 if (traits == UndefinedPixelTrait)
2003 if ((traits & UpdatePixelTrait) == 0)
2005 if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
2007 depth=channel_statistics[channel].depth;
2008 range=GetQuantumRange(depth);
2009 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2010 range) ? MagickTrue : MagickFalse;
2011 if (status != MagickFalse)
2013 channel_statistics[channel].depth++;
2018 if ((double) p[i] < channel_statistics[channel].minima)
2019 channel_statistics[channel].minima=(double) p[i];
2020 if ((double) p[i] > channel_statistics[channel].maxima)
2021 channel_statistics[channel].maxima=(double) p[i];
2022 channel_statistics[channel].sum+=p[i];
2023 channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
2024 channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
2025 channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
2027 channel_statistics[channel].area++;
2028 if ((double) p[i] < channel_statistics[CompositePixelChannel].minima)
2029 channel_statistics[CompositePixelChannel].minima=(double) p[i];
2030 if ((double) p[i] > channel_statistics[CompositePixelChannel].maxima)
2031 channel_statistics[CompositePixelChannel].maxima=(double) p[i];
2032 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2033 ClampToQuantum((double) p[i]))+i]++;
2034 channel_statistics[CompositePixelChannel].sum+=(double) p[i];
2035 channel_statistics[CompositePixelChannel].sum_squared+=(double)
2037 channel_statistics[CompositePixelChannel].sum_cubed+=(double)
2039 channel_statistics[CompositePixelChannel].sum_fourth_power+=(double)
2040 p[i]*p[i]*p[i]*p[i];
2041 channel_statistics[CompositePixelChannel].area++;
2043 p+=GetPixelChannels(image);
2046 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2049 Normalize pixel statistics.
2051 area=PerceptibleReciprocal(channel_statistics[i].area);
2052 channel_statistics[i].sum*=area;
2053 channel_statistics[i].sum_squared*=area;
2054 channel_statistics[i].sum_cubed*=area;
2055 channel_statistics[i].sum_fourth_power*=area;
2056 channel_statistics[i].mean=channel_statistics[i].sum;
2057 channel_statistics[i].variance=channel_statistics[i].sum_squared;
2058 standard_deviation=sqrt(channel_statistics[i].variance-
2059 (channel_statistics[i].mean*channel_statistics[i].mean));
2060 standard_deviation=sqrt(PerceptibleReciprocal(channel_statistics[i].area-
2061 1.0)*channel_statistics[i].area*standard_deviation*standard_deviation);
2062 channel_statistics[i].standard_deviation=standard_deviation;
2064 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2073 Compute pixel entropy.
2075 PixelChannel channel=GetPixelChannelChannel(image,i);
2077 for (j=0; j <= (ssize_t) MaxMap; j++)
2078 if (histogram[GetPixelChannels(image)*j+i] > 0.0)
2080 area=PerceptibleReciprocal(channel_statistics[channel].area);
2081 for (j=0; j <= (ssize_t) MaxMap; j++)
2086 count=area*histogram[GetPixelChannels(image)*j+i];
2087 if (number_bins > MagickEpsilon)
2089 channel_statistics[channel].entropy+=-count*MagickLog10(count)/
2090 MagickLog10(number_bins);
2091 channel_statistics[CompositePixelChannel].entropy+=-count*
2092 MagickLog10(count)/MagickLog10(number_bins)/
2093 GetPixelChannels(image);
2097 histogram=(double *) RelinquishMagickMemory(histogram);
2098 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2101 Compute kurtosis & skewness statistics.
2103 standard_deviation=PerceptibleReciprocal(
2104 channel_statistics[i].standard_deviation);
2105 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
2106 channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
2107 channel_statistics[i].mean*channel_statistics[i].mean*
2108 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2109 standard_deviation);
2110 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
2111 channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
2112 channel_statistics[i].mean*channel_statistics[i].mean*
2113 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
2114 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
2115 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2116 standard_deviation*standard_deviation)-3.0;
2118 if (y < (ssize_t) image->rows)
2119 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2120 channel_statistics);
2121 return(channel_statistics);
2125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2129 % P o l y n o m i a l I m a g e %
2133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2135 % PolynomialImage() returns a new image where each pixel is the sum of the
2136 % pixels in the image sequence after applying its corresponding terms
2137 % (coefficient and degree pairs).
2139 % The format of the PolynomialImage method is:
2141 % Image *PolynomialImage(const Image *images,const size_t number_terms,
2142 % const double *terms,ExceptionInfo *exception)
2144 % A description of each parameter follows:
2146 % o images: the image sequence.
2148 % o number_terms: the number of terms in the list. The actual list length
2149 % is 2 x number_terms + 1 (the constant).
2151 % o terms: the list of polynomial coefficients and degree pairs and a
2154 % o exception: return any errors or warnings in this structure.
2158 MagickExport Image *PolynomialImage(const Image *images,
2159 const size_t number_terms,const double *terms,ExceptionInfo *exception)
2161 #define PolynomialImageTag "Polynomial/Image"
2176 **magick_restrict polynomial_pixels;
2184 assert(images != (Image *) NULL);
2185 assert(images->signature == MagickCoreSignature);
2186 if (images->debug != MagickFalse)
2187 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2188 assert(exception != (ExceptionInfo *) NULL);
2189 assert(exception->signature == MagickCoreSignature);
2190 image=CloneImage(images,images->columns,images->rows,MagickTrue,
2192 if (image == (Image *) NULL)
2193 return((Image *) NULL);
2194 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2196 image=DestroyImage(image);
2197 return((Image *) NULL);
2199 number_images=GetImageListLength(images);
2200 polynomial_pixels=AcquirePixelThreadSet(images);
2201 if (polynomial_pixels == (PixelChannels **) NULL)
2203 image=DestroyImage(image);
2204 (void) ThrowMagickException(exception,GetMagickModule(),
2205 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2206 return((Image *) NULL);
2209 Polynomial image pixels.
2213 polynomial_view=AcquireAuthenticCacheView(image,exception);
2214 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2215 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2216 magick_threads(image,image,image->rows,1)
2218 for (y=0; y < (ssize_t) image->rows; y++)
2227 id = GetOpenMPThreadId();
2233 register PixelChannels
2242 if (status == MagickFalse)
2244 q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2246 if (q == (Quantum *) NULL)
2251 polynomial_pixel=polynomial_pixels[id];
2252 for (j=0; j < (ssize_t) image->columns; j++)
2253 for (i=0; i < MaxPixelChannels; i++)
2254 polynomial_pixel[j].channel[i]=0.0;
2256 for (j=0; j < (ssize_t) number_images; j++)
2258 register const Quantum
2261 if (j >= (ssize_t) number_terms)
2263 image_view=AcquireVirtualCacheView(next,exception);
2264 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2265 if (p == (const Quantum *) NULL)
2267 image_view=DestroyCacheView(image_view);
2270 for (x=0; x < (ssize_t) image->columns; x++)
2275 if (GetPixelWriteMask(next,p) == 0)
2277 p+=GetPixelChannels(next);
2280 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2286 PixelChannel channel=GetPixelChannelChannel(image,i);
2287 PixelTrait traits=GetPixelChannelTraits(next,channel);
2288 PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
2289 if ((traits == UndefinedPixelTrait) ||
2290 (polynomial_traits == UndefinedPixelTrait))
2292 if ((traits & UpdatePixelTrait) == 0)
2294 coefficient=(MagickRealType) terms[2*j];
2295 degree=(MagickRealType) terms[(j << 1)+1];
2296 polynomial_pixel[x].channel[i]+=coefficient*
2297 pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
2299 p+=GetPixelChannels(next);
2301 image_view=DestroyCacheView(image_view);
2302 next=GetNextImageInList(next);
2304 for (x=0; x < (ssize_t) image->columns; x++)
2309 if (GetPixelWriteMask(image,q) == 0)
2311 q+=GetPixelChannels(image);
2314 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2316 PixelChannel channel=GetPixelChannelChannel(image,i);
2317 PixelTrait traits=GetPixelChannelTraits(image,channel);
2318 if (traits == UndefinedPixelTrait)
2320 if ((traits & UpdatePixelTrait) == 0)
2322 q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
2324 q+=GetPixelChannels(image);
2326 if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2328 if (images->progress_monitor != (MagickProgressMonitor) NULL)
2333 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2334 #pragma omp critical (MagickCore_PolynomialImages)
2336 proceed=SetImageProgress(images,PolynomialImageTag,progress++,
2338 if (proceed == MagickFalse)
2342 polynomial_view=DestroyCacheView(polynomial_view);
2343 polynomial_pixels=DestroyPixelThreadSet(polynomial_pixels);
2344 if (status == MagickFalse)
2345 image=DestroyImage(image);
2350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2354 % S t a t i s t i c I m a g e %
2358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2360 % StatisticImage() makes each pixel the min / max / median / mode / etc. of
2361 % the neighborhood of the specified width and height.
2363 % The format of the StatisticImage method is:
2365 % Image *StatisticImage(const Image *image,const StatisticType type,
2366 % const size_t width,const size_t height,ExceptionInfo *exception)
2368 % A description of each parameter follows:
2370 % o image: the image.
2372 % o type: the statistic type (median, mode, etc.).
2374 % o width: the width of the pixel neighborhood.
2376 % o height: the height of the pixel neighborhood.
2378 % o exception: return any errors or warnings in this structure.
2382 typedef struct _SkipNode
2390 typedef struct _SkipList
2399 typedef struct _PixelList
2412 static PixelList *DestroyPixelList(PixelList *pixel_list)
2414 if (pixel_list == (PixelList *) NULL)
2415 return((PixelList *) NULL);
2416 if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2417 pixel_list->skip_list.nodes=(SkipNode *) RelinquishAlignedMemory(
2418 pixel_list->skip_list.nodes);
2419 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2423 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2428 assert(pixel_list != (PixelList **) NULL);
2429 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2430 if (pixel_list[i] != (PixelList *) NULL)
2431 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2432 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2436 static PixelList *AcquirePixelList(const size_t width,const size_t height)
2441 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2442 if (pixel_list == (PixelList *) NULL)
2444 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2445 pixel_list->length=width*height;
2446 pixel_list->skip_list.nodes=(SkipNode *) AcquireAlignedMemory(65537UL,
2447 sizeof(*pixel_list->skip_list.nodes));
2448 if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2449 return(DestroyPixelList(pixel_list));
2450 (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
2451 sizeof(*pixel_list->skip_list.nodes));
2452 pixel_list->signature=MagickCoreSignature;
2456 static PixelList **AcquirePixelListThreadSet(const size_t width,
2457 const size_t height)
2468 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2469 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2470 sizeof(*pixel_list));
2471 if (pixel_list == (PixelList **) NULL)
2472 return((PixelList **) NULL);
2473 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2474 for (i=0; i < (ssize_t) number_threads; i++)
2476 pixel_list[i]=AcquirePixelList(width,height);
2477 if (pixel_list[i] == (PixelList *) NULL)
2478 return(DestroyPixelListThreadSet(pixel_list));
2483 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
2496 Initialize the node.
2498 p=(&pixel_list->skip_list);
2499 p->nodes[color].signature=pixel_list->signature;
2500 p->nodes[color].count=1;
2502 Determine where it belongs in the list.
2505 for (level=p->level; level >= 0; level--)
2507 while (p->nodes[search].next[level] < color)
2508 search=p->nodes[search].next[level];
2509 update[level]=search;
2512 Generate a pseudo-random level for this node.
2514 for (level=0; ; level++)
2516 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2517 if ((pixel_list->seed & 0x300) != 0x300)
2522 if (level > (p->level+2))
2525 If we're raising the list's level, link back to the root node.
2527 while (level > p->level)
2530 update[p->level]=65536UL;
2533 Link the node into the skip-list.
2537 p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2538 p->nodes[update[level]].next[level]=color;
2539 } while (level-- > 0);
2542 static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
2555 Find the maximum value for each of the color.
2557 p=(&pixel_list->skip_list);
2560 maximum=p->nodes[color].next[0];
2563 color=p->nodes[color].next[0];
2564 if (color > maximum)
2566 count+=p->nodes[color].count;
2567 } while (count < (ssize_t) pixel_list->length);
2568 *pixel=ScaleShortToQuantum((unsigned short) maximum);
2571 static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
2586 Find the mean value for each of the color.
2588 p=(&pixel_list->skip_list);
2594 color=p->nodes[color].next[0];
2595 sum+=(double) p->nodes[color].count*color;
2596 count+=p->nodes[color].count;
2597 } while (count < (ssize_t) pixel_list->length);
2598 sum/=pixel_list->length;
2599 *pixel=ScaleShortToQuantum((unsigned short) sum);
2602 static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2614 Find the median value for each of the color.
2616 p=(&pixel_list->skip_list);
2621 color=p->nodes[color].next[0];
2622 count+=p->nodes[color].count;
2623 } while (count <= (ssize_t) (pixel_list->length >> 1));
2624 *pixel=ScaleShortToQuantum((unsigned short) color);
2627 static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
2640 Find the minimum value for each of the color.
2642 p=(&pixel_list->skip_list);
2645 minimum=p->nodes[color].next[0];
2648 color=p->nodes[color].next[0];
2649 if (color < minimum)
2651 count+=p->nodes[color].count;
2652 } while (count < (ssize_t) pixel_list->length);
2653 *pixel=ScaleShortToQuantum((unsigned short) minimum);
2656 static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2670 Make each pixel the 'predominant color' of the specified neighborhood.
2672 p=(&pixel_list->skip_list);
2675 max_count=p->nodes[mode].count;
2679 color=p->nodes[color].next[0];
2680 if (p->nodes[color].count > max_count)
2683 max_count=p->nodes[mode].count;
2685 count+=p->nodes[color].count;
2686 } while (count < (ssize_t) pixel_list->length);
2687 *pixel=ScaleShortToQuantum((unsigned short) mode);
2690 static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2704 Finds the non peak value for each of the colors.
2706 p=(&pixel_list->skip_list);
2708 next=p->nodes[color].next[0];
2714 next=p->nodes[color].next[0];
2715 count+=p->nodes[color].count;
2716 } while (count <= (ssize_t) (pixel_list->length >> 1));
2717 if ((previous == 65536UL) && (next != 65536UL))
2720 if ((previous != 65536UL) && (next == 65536UL))
2722 *pixel=ScaleShortToQuantum((unsigned short) color);
2725 static inline void GetRootMeanSquarePixelList(PixelList *pixel_list,
2741 Find the root mean square value for each of the color.
2743 p=(&pixel_list->skip_list);
2749 color=p->nodes[color].next[0];
2750 sum+=(double) (p->nodes[color].count*color*color);
2751 count+=p->nodes[color].count;
2752 } while (count < (ssize_t) pixel_list->length);
2753 sum/=pixel_list->length;
2754 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum));
2757 static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2774 Find the standard-deviation value for each of the color.
2776 p=(&pixel_list->skip_list);
2786 color=p->nodes[color].next[0];
2787 sum+=(double) p->nodes[color].count*color;
2788 for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2789 sum_squared+=((double) color)*((double) color);
2790 count+=p->nodes[color].count;
2791 } while (count < (ssize_t) pixel_list->length);
2792 sum/=pixel_list->length;
2793 sum_squared/=pixel_list->length;
2794 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2797 static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
2805 index=ScaleQuantumToShort(pixel);
2806 signature=pixel_list->skip_list.nodes[index].signature;
2807 if (signature == pixel_list->signature)
2809 pixel_list->skip_list.nodes[index].count++;
2812 AddNodePixelList(pixel_list,index);
2815 static void ResetPixelList(PixelList *pixel_list)
2827 Reset the skip-list.
2829 p=(&pixel_list->skip_list);
2830 root=p->nodes+65536UL;
2832 for (level=0; level < 9; level++)
2833 root->next[level]=65536UL;
2834 pixel_list->seed=pixel_list->signature++;
2837 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2838 const size_t width,const size_t height,ExceptionInfo *exception)
2840 #define StatisticImageTag "Statistic/Image"
2856 **magick_restrict pixel_list;
2863 Initialize statistics image attributes.
2865 assert(image != (Image *) NULL);
2866 assert(image->signature == MagickCoreSignature);
2867 if (image->debug != MagickFalse)
2868 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2869 assert(exception != (ExceptionInfo *) NULL);
2870 assert(exception->signature == MagickCoreSignature);
2871 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2873 if (statistic_image == (Image *) NULL)
2874 return((Image *) NULL);
2875 status=SetImageStorageClass(statistic_image,DirectClass,exception);
2876 if (status == MagickFalse)
2878 statistic_image=DestroyImage(statistic_image);
2879 return((Image *) NULL);
2881 pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
2882 if (pixel_list == (PixelList **) NULL)
2884 statistic_image=DestroyImage(statistic_image);
2885 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2888 Make each pixel the min / max / median / mode / etc. of the neighborhood.
2890 center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
2891 (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
2894 image_view=AcquireVirtualCacheView(image,exception);
2895 statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
2896 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2897 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2898 magick_threads(image,statistic_image,statistic_image->rows,1)
2900 for (y=0; y < (ssize_t) statistic_image->rows; y++)
2903 id = GetOpenMPThreadId();
2905 register const Quantum
2914 if (status == MagickFalse)
2916 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
2917 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
2918 MagickMax(height,1),exception);
2919 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
2920 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2925 for (x=0; x < (ssize_t) statistic_image->columns; x++)
2930 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2935 register const Quantum
2936 *magick_restrict pixels;
2944 PixelChannel channel=GetPixelChannelChannel(image,i);
2945 PixelTrait traits=GetPixelChannelTraits(image,channel);
2946 PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
2948 if ((traits == UndefinedPixelTrait) ||
2949 (statistic_traits == UndefinedPixelTrait))
2951 if (((statistic_traits & CopyPixelTrait) != 0) ||
2952 (GetPixelWriteMask(image,p) == 0))
2954 SetPixelChannel(statistic_image,channel,p[center+i],q);
2957 if ((statistic_traits & UpdatePixelTrait) == 0)
2960 ResetPixelList(pixel_list[id]);
2961 for (v=0; v < (ssize_t) MagickMax(height,1); v++)
2963 for (u=0; u < (ssize_t) MagickMax(width,1); u++)
2965 InsertPixelList(pixels[i],pixel_list[id]);
2966 pixels+=GetPixelChannels(image);
2968 pixels+=GetPixelChannels(image)*image->columns;
2972 case GradientStatistic:
2978 GetMinimumPixelList(pixel_list[id],&pixel);
2979 minimum=(double) pixel;
2980 GetMaximumPixelList(pixel_list[id],&pixel);
2981 maximum=(double) pixel;
2982 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
2985 case MaximumStatistic:
2987 GetMaximumPixelList(pixel_list[id],&pixel);
2992 GetMeanPixelList(pixel_list[id],&pixel);
2995 case MedianStatistic:
2998 GetMedianPixelList(pixel_list[id],&pixel);
3001 case MinimumStatistic:
3003 GetMinimumPixelList(pixel_list[id],&pixel);
3008 GetModePixelList(pixel_list[id],&pixel);
3011 case NonpeakStatistic:
3013 GetNonpeakPixelList(pixel_list[id],&pixel);
3016 case RootMeanSquareStatistic:
3018 GetRootMeanSquarePixelList(pixel_list[id],&pixel);
3021 case StandardDeviationStatistic:
3023 GetStandardDeviationPixelList(pixel_list[id],&pixel);
3027 SetPixelChannel(statistic_image,channel,pixel,q);
3029 p+=GetPixelChannels(image);
3030 q+=GetPixelChannels(statistic_image);
3032 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3034 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3039 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3040 #pragma omp critical (MagickCore_StatisticImage)
3042 proceed=SetImageProgress(image,StatisticImageTag,progress++,
3044 if (proceed == MagickFalse)
3048 statistic_view=DestroyCacheView(statistic_view);
3049 image_view=DestroyCacheView(image_view);
3050 pixel_list=DestroyPixelListThreadSet(pixel_list);
3051 if (status == MagickFalse)
3052 statistic_image=DestroyImage(statistic_image);
3053 return(statistic_image);