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-2019 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://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(const Image *images,
141 PixelChannels **pixels)
149 assert(pixels != (PixelChannels **) NULL);
150 rows=MagickMax(GetImageListLength(images),
151 (size_t) GetMagickResourceLimit(ThreadResource));
152 for (i=0; i < (ssize_t) rows; i++)
153 if (pixels[i] != (PixelChannels *) NULL)
154 pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]);
155 pixels=(PixelChannels **) RelinquishMagickMemory(pixels);
159 static PixelChannels **AcquirePixelThreadSet(const Image *images)
174 rows=MagickMax(GetImageListLength(images),
175 (size_t) GetMagickResourceLimit(ThreadResource));
176 pixels=(PixelChannels **) AcquireQuantumMemory(rows,sizeof(*pixels));
177 if (pixels == (PixelChannels **) NULL)
178 return((PixelChannels **) NULL);
179 (void) memset(pixels,0,rows*sizeof(*pixels));
180 columns=MagickMax(GetImageListLength(images),MaxPixelChannels);
181 for (next=images; next != (Image *) NULL; next=next->next)
182 columns=MagickMax(next->columns,columns);
183 for (i=0; i < (ssize_t) rows; i++)
188 pixels[i]=(PixelChannels *) AcquireQuantumMemory(columns,sizeof(**pixels));
189 if (pixels[i] == (PixelChannels *) NULL)
190 return(DestroyPixelThreadSet(images,pixels));
191 for (j=0; j < (ssize_t) columns; j++)
196 for (k=0; k < MaxPixelChannels; k++)
197 pixels[i][j].channel[k]=0.0;
203 static inline double EvaluateMax(const double x,const double y)
210 #if defined(__cplusplus) || defined(c_plusplus)
214 static int IntensityCompare(const void *x,const void *y)
226 color_1=(const PixelChannels *) x;
227 color_2=(const PixelChannels *) y;
229 for (i=0; i < MaxPixelChannels; i++)
230 distance+=color_1->channel[i]-(double) color_2->channel[i];
231 return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
234 #if defined(__cplusplus) || defined(c_plusplus)
238 static double ApplyEvaluateOperator(RandomInfo *random_info,const Quantum pixel,
239 const MagickEvaluateOperator op,const double value)
250 case UndefinedEvaluateOperator:
252 case AbsEvaluateOperator:
254 result=(double) fabs((double) (pixel+value));
257 case AddEvaluateOperator:
259 result=(double) (pixel+value);
262 case AddModulusEvaluateOperator:
265 This returns a 'floored modulus' of the addition which is a positive
266 result. It differs from % or fmod() that returns a 'truncated modulus'
267 result, where floor() is replaced by trunc() and could return a
268 negative result (which is clipped).
271 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
274 case AndEvaluateOperator:
276 result=(double) ((ssize_t) pixel & (ssize_t) (value+0.5));
279 case CosineEvaluateOperator:
281 result=(double) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
282 QuantumScale*pixel*value))+0.5));
285 case DivideEvaluateOperator:
287 result=pixel/(value == 0.0 ? 1.0 : value);
290 case ExponentialEvaluateOperator:
292 result=(double) (QuantumRange*exp((double) (value*QuantumScale*pixel)));
295 case GaussianNoiseEvaluateOperator:
297 result=(double) GenerateDifferentialNoise(random_info,pixel,
298 GaussianNoise,value);
301 case ImpulseNoiseEvaluateOperator:
303 result=(double) GenerateDifferentialNoise(random_info,pixel,ImpulseNoise,
307 case LaplacianNoiseEvaluateOperator:
309 result=(double) GenerateDifferentialNoise(random_info,pixel,
310 LaplacianNoise,value);
313 case LeftShiftEvaluateOperator:
315 result=(double) pixel;
316 for (i=0; i < (ssize_t) value; i++)
320 case LogEvaluateOperator:
322 if ((QuantumScale*pixel) >= MagickEpsilon)
323 result=(double) (QuantumRange*log((double) (QuantumScale*value*pixel+
324 1.0))/log((double) (value+1.0)));
327 case MaxEvaluateOperator:
329 result=(double) EvaluateMax((double) pixel,value);
332 case MeanEvaluateOperator:
334 result=(double) (pixel+value);
337 case MedianEvaluateOperator:
339 result=(double) (pixel+value);
342 case MinEvaluateOperator:
344 result=(double) MagickMin((double) pixel,value);
347 case MultiplicativeNoiseEvaluateOperator:
349 result=(double) GenerateDifferentialNoise(random_info,pixel,
350 MultiplicativeGaussianNoise,value);
353 case MultiplyEvaluateOperator:
355 result=(double) (value*pixel);
358 case OrEvaluateOperator:
360 result=(double) ((ssize_t) pixel | (ssize_t) (value+0.5));
363 case PoissonNoiseEvaluateOperator:
365 result=(double) GenerateDifferentialNoise(random_info,pixel,PoissonNoise,
369 case PowEvaluateOperator:
372 result=(double) -(QuantumRange*pow((double) -(QuantumScale*pixel),
375 result=(double) (QuantumRange*pow((double) (QuantumScale*pixel),
379 case RightShiftEvaluateOperator:
381 result=(double) pixel;
382 for (i=0; i < (ssize_t) value; i++)
386 case RootMeanSquareEvaluateOperator:
388 result=(double) (pixel*pixel+value);
391 case SetEvaluateOperator:
396 case SineEvaluateOperator:
398 result=(double) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
399 QuantumScale*pixel*value))+0.5));
402 case SubtractEvaluateOperator:
404 result=(double) (pixel-value);
407 case SumEvaluateOperator:
409 result=(double) (pixel+value);
412 case ThresholdEvaluateOperator:
414 result=(double) (((double) pixel <= value) ? 0 : QuantumRange);
417 case ThresholdBlackEvaluateOperator:
419 result=(double) (((double) pixel <= value) ? 0 : pixel);
422 case ThresholdWhiteEvaluateOperator:
424 result=(double) (((double) pixel > value) ? QuantumRange : pixel);
427 case UniformNoiseEvaluateOperator:
429 result=(double) GenerateDifferentialNoise(random_info,pixel,UniformNoise,
433 case XorEvaluateOperator:
435 result=(double) ((ssize_t) pixel ^ (ssize_t) (value+0.5));
442 static Image *AcquireImageCanvas(const Image *images,ExceptionInfo *exception)
453 columns=images->columns;
455 for (p=images; p != (Image *) NULL; p=p->next)
457 if (p->number_channels > q->number_channels)
459 if (p->columns > columns)
464 return(CloneImage(q,columns,rows,MagickTrue,exception));
467 MagickExport Image *EvaluateImages(const Image *images,
468 const MagickEvaluateOperator op,ExceptionInfo *exception)
470 #define EvaluateImageTag "Evaluate/Image"
485 **magick_restrict evaluate_pixels;
488 **magick_restrict random_info;
496 #if defined(MAGICKCORE_OPENMP_SUPPORT)
501 assert(images != (Image *) NULL);
502 assert(images->signature == MagickCoreSignature);
503 if (images->debug != MagickFalse)
504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
505 assert(exception != (ExceptionInfo *) NULL);
506 assert(exception->signature == MagickCoreSignature);
507 image=AcquireImageCanvas(images,exception);
508 if (image == (Image *) NULL)
509 return((Image *) NULL);
510 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
512 image=DestroyImage(image);
513 return((Image *) NULL);
515 number_images=GetImageListLength(images);
516 evaluate_pixels=AcquirePixelThreadSet(images);
517 if (evaluate_pixels == (PixelChannels **) NULL)
519 image=DestroyImage(image);
520 (void) ThrowMagickException(exception,GetMagickModule(),
521 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
522 return((Image *) NULL);
525 Evaluate image pixels.
529 random_info=AcquireRandomInfoThreadSet();
530 evaluate_view=AcquireAuthenticCacheView(image,exception);
531 if (op == MedianEvaluateOperator)
533 #if defined(MAGICKCORE_OPENMP_SUPPORT)
534 key=GetRandomSecretKey(random_info[0]);
535 #pragma omp parallel for schedule(static) shared(progress,status) \
536 magick_number_threads(image,images,image->rows,key == ~0UL)
538 for (y=0; y < (ssize_t) image->rows; y++)
547 id = GetOpenMPThreadId();
549 register PixelChannels
558 if (status == MagickFalse)
560 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
562 if (q == (Quantum *) NULL)
567 evaluate_pixel=evaluate_pixels[id];
568 for (x=0; x < (ssize_t) image->columns; x++)
574 for (j=0; j < (ssize_t) number_images; j++)
575 for (k=0; k < MaxPixelChannels; k++)
576 evaluate_pixel[j].channel[k]=0.0;
578 for (j=0; j < (ssize_t) number_images; j++)
580 register const Quantum
586 image_view=AcquireVirtualCacheView(next,exception);
587 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
588 if (p == (const Quantum *) NULL)
590 image_view=DestroyCacheView(image_view);
593 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
595 PixelChannel channel = GetPixelChannelChannel(image,i);
596 PixelTrait traits = GetPixelChannelTraits(next,channel);
597 PixelTrait evaluate_traits = GetPixelChannelTraits(image,channel);
598 if ((traits == UndefinedPixelTrait) ||
599 (evaluate_traits == UndefinedPixelTrait))
601 if ((traits & UpdatePixelTrait) == 0)
603 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(
604 random_info[id],GetPixelChannel(next,channel,p),op,
605 evaluate_pixel[j].channel[i]);
607 image_view=DestroyCacheView(image_view);
608 next=GetNextImageInList(next);
610 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
612 for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
613 q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]);
614 q+=GetPixelChannels(image);
616 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
618 if (images->progress_monitor != (MagickProgressMonitor) NULL)
623 #if defined(MAGICKCORE_OPENMP_SUPPORT)
627 proceed=SetImageProgress(images,EvaluateImageTag,progress,
629 if (proceed == MagickFalse)
636 #if defined(MAGICKCORE_OPENMP_SUPPORT)
637 key=GetRandomSecretKey(random_info[0]);
638 #pragma omp parallel for schedule(static) shared(progress,status) \
639 magick_number_threads(image,images,image->rows,key == ~0UL)
641 for (y=0; y < (ssize_t) image->rows; y++)
650 id = GetOpenMPThreadId();
656 register PixelChannels
665 if (status == MagickFalse)
667 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
669 if (q == (Quantum *) NULL)
674 evaluate_pixel=evaluate_pixels[id];
675 for (j=0; j < (ssize_t) image->columns; j++)
676 for (i=0; i < MaxPixelChannels; i++)
677 evaluate_pixel[j].channel[i]=0.0;
679 for (j=0; j < (ssize_t) number_images; j++)
681 register const Quantum
684 image_view=AcquireVirtualCacheView(next,exception);
685 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
687 if (p == (const Quantum *) NULL)
689 image_view=DestroyCacheView(image_view);
692 for (x=0; x < (ssize_t) image->columns; x++)
697 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
699 PixelChannel channel = GetPixelChannelChannel(image,i);
700 PixelTrait traits = GetPixelChannelTraits(next,channel);
701 PixelTrait evaluate_traits = GetPixelChannelTraits(image,channel);
702 if ((traits == UndefinedPixelTrait) ||
703 (evaluate_traits == UndefinedPixelTrait))
705 if ((traits & UpdatePixelTrait) == 0)
707 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
708 random_info[id],GetPixelChannel(next,channel,p),j == 0 ?
709 AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
711 p+=GetPixelChannels(next);
713 image_view=DestroyCacheView(image_view);
714 next=GetNextImageInList(next);
716 for (x=0; x < (ssize_t) image->columns; x++)
723 case MeanEvaluateOperator:
725 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
726 evaluate_pixel[x].channel[i]/=(double) number_images;
729 case MultiplyEvaluateOperator:
731 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
736 for (j=0; j < (ssize_t) (number_images-1); j++)
737 evaluate_pixel[x].channel[i]*=QuantumScale;
741 case RootMeanSquareEvaluateOperator:
743 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
744 evaluate_pixel[x].channel[i]=sqrt(evaluate_pixel[x].channel[i]/
752 for (x=0; x < (ssize_t) image->columns; x++)
757 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
759 PixelChannel channel = GetPixelChannelChannel(image,i);
760 PixelTrait traits = GetPixelChannelTraits(image,channel);
761 if (traits == UndefinedPixelTrait)
763 if ((traits & UpdatePixelTrait) == 0)
765 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
767 q+=GetPixelChannels(image);
769 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
771 if (images->progress_monitor != (MagickProgressMonitor) NULL)
776 #if defined(MAGICKCORE_OPENMP_SUPPORT)
780 proceed=SetImageProgress(images,EvaluateImageTag,progress,
782 if (proceed == MagickFalse)
787 evaluate_view=DestroyCacheView(evaluate_view);
788 evaluate_pixels=DestroyPixelThreadSet(images,evaluate_pixels);
789 random_info=DestroyRandomInfoThreadSet(random_info);
790 if (status == MagickFalse)
791 image=DestroyImage(image);
795 MagickExport MagickBooleanType EvaluateImage(Image *image,
796 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
808 **magick_restrict random_info;
813 #if defined(MAGICKCORE_OPENMP_SUPPORT)
818 assert(image != (Image *) NULL);
819 assert(image->signature == MagickCoreSignature);
820 if (image->debug != MagickFalse)
821 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
822 assert(exception != (ExceptionInfo *) NULL);
823 assert(exception->signature == MagickCoreSignature);
824 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
828 random_info=AcquireRandomInfoThreadSet();
829 image_view=AcquireAuthenticCacheView(image,exception);
830 #if defined(MAGICKCORE_OPENMP_SUPPORT)
831 key=GetRandomSecretKey(random_info[0]);
832 #pragma omp parallel for schedule(static) shared(progress,status) \
833 magick_number_threads(image,image,image->rows,key == ~0UL)
835 for (y=0; y < (ssize_t) image->rows; y++)
838 id = GetOpenMPThreadId();
846 if (status == MagickFalse)
848 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
849 if (q == (Quantum *) NULL)
854 for (x=0; x < (ssize_t) image->columns; x++)
862 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
864 PixelChannel channel = GetPixelChannelChannel(image,i);
865 PixelTrait traits = GetPixelChannelTraits(image,channel);
866 if (traits == UndefinedPixelTrait)
868 if ((traits & CopyPixelTrait) != 0)
870 if ((traits & UpdatePixelTrait) == 0)
872 result=ApplyEvaluateOperator(random_info[id],q[i],op,value);
873 if (op == MeanEvaluateOperator)
875 q[i]=ClampToQuantum(result);
877 q+=GetPixelChannels(image);
879 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
881 if (image->progress_monitor != (MagickProgressMonitor) NULL)
886 #if defined(MAGICKCORE_OPENMP_SUPPORT)
890 proceed=SetImageProgress(image,EvaluateImageTag,progress,image->rows);
891 if (proceed == MagickFalse)
895 image_view=DestroyCacheView(image_view);
896 random_info=DestroyRandomInfoThreadSet(random_info);
901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905 % F u n c t i o n I m a g e %
909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911 % FunctionImage() applies a value to the image with an arithmetic, relational,
912 % or logical operator to an image. Use these operations to lighten or darken
913 % an image, to increase or decrease contrast in an image, or to produce the
914 % "negative" of an image.
916 % The format of the FunctionImage method is:
918 % MagickBooleanType FunctionImage(Image *image,
919 % const MagickFunction function,const ssize_t number_parameters,
920 % const double *parameters,ExceptionInfo *exception)
922 % A description of each parameter follows:
924 % o image: the image.
926 % o function: A channel function.
928 % o parameters: one or more parameters.
930 % o exception: return any errors or warnings in this structure.
934 static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
935 const size_t number_parameters,const double *parameters,
936 ExceptionInfo *exception)
948 case PolynomialFunction:
951 Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+
955 for (i=0; i < (ssize_t) number_parameters; i++)
956 result=result*QuantumScale*pixel+parameters[i];
957 result*=QuantumRange;
960 case SinusoidFunction:
969 Sinusoid: frequency, phase, amplitude, bias.
971 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
972 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
973 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
974 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
975 result=(double) (QuantumRange*(amplitude*sin((double) (2.0*
976 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
988 Arcsin (peged at range limits for invalid results): width, center,
991 width=(number_parameters >= 1) ? parameters[0] : 1.0;
992 center=(number_parameters >= 2) ? parameters[1] : 0.5;
993 range=(number_parameters >= 3) ? parameters[2] : 1.0;
994 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
995 result=2.0/width*(QuantumScale*pixel-center);
996 if ( result <= -1.0 )
997 result=bias-range/2.0;
1000 result=bias+range/2.0;
1002 result=(double) (range/MagickPI*asin((double) result)+bias);
1003 result*=QuantumRange;
1006 case ArctanFunction:
1015 Arctan: slope, center, range, and bias.
1017 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
1018 center=(number_parameters >= 2) ? parameters[1] : 0.5;
1019 range=(number_parameters >= 3) ? parameters[2] : 1.0;
1020 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
1021 result=(double) (MagickPI*slope*(QuantumScale*pixel-center));
1022 result=(double) (QuantumRange*(range/MagickPI*atan((double)
1026 case UndefinedFunction:
1029 return(ClampToQuantum(result));
1032 MagickExport MagickBooleanType FunctionImage(Image *image,
1033 const MagickFunction function,const size_t number_parameters,
1034 const double *parameters,ExceptionInfo *exception)
1036 #define FunctionImageTag "Function/Image "
1050 assert(image != (Image *) NULL);
1051 assert(image->signature == MagickCoreSignature);
1052 if (image->debug != MagickFalse)
1053 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1054 assert(exception != (ExceptionInfo *) NULL);
1055 assert(exception->signature == MagickCoreSignature);
1056 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1057 if (AccelerateFunctionImage(image,function,number_parameters,parameters,
1058 exception) != MagickFalse)
1061 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1062 return(MagickFalse);
1065 image_view=AcquireAuthenticCacheView(image,exception);
1066 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1067 #pragma omp parallel for schedule(static) shared(progress,status) \
1068 magick_number_threads(image,image,image->rows,1)
1070 for (y=0; y < (ssize_t) image->rows; y++)
1078 if (status == MagickFalse)
1080 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1081 if (q == (Quantum *) NULL)
1086 for (x=0; x < (ssize_t) image->columns; x++)
1091 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1093 PixelChannel channel = GetPixelChannelChannel(image,i);
1094 PixelTrait traits = GetPixelChannelTraits(image,channel);
1095 if (traits == UndefinedPixelTrait)
1097 if ((traits & UpdatePixelTrait) == 0)
1099 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1102 q+=GetPixelChannels(image);
1104 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1106 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1111 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1115 proceed=SetImageProgress(image,FunctionImageTag,progress,image->rows);
1116 if (proceed == MagickFalse)
1120 image_view=DestroyCacheView(image_view);
1125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129 % G e t I m a g e E n t r o p y %
1133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135 % GetImageEntropy() returns the entropy of one or more image channels.
1137 % The format of the GetImageEntropy method is:
1139 % MagickBooleanType GetImageEntropy(const Image *image,double *entropy,
1140 % ExceptionInfo *exception)
1142 % A description of each parameter follows:
1144 % o image: the image.
1146 % o entropy: the average entropy of the selected channels.
1148 % o exception: return any errors or warnings in this structure.
1151 MagickExport MagickBooleanType GetImageEntropy(const Image *image,
1152 double *entropy,ExceptionInfo *exception)
1155 *channel_statistics;
1157 assert(image != (Image *) NULL);
1158 assert(image->signature == MagickCoreSignature);
1159 if (image->debug != MagickFalse)
1160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1161 channel_statistics=GetImageStatistics(image,exception);
1162 if (channel_statistics == (ChannelStatistics *) NULL)
1163 return(MagickFalse);
1164 *entropy=channel_statistics[CompositePixelChannel].entropy;
1165 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1166 channel_statistics);
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 % G e t I m a g e E x t r e m a %
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1181 % GetImageExtrema() returns the extrema of one or more image channels.
1183 % The format of the GetImageExtrema method is:
1185 % MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1186 % size_t *maxima,ExceptionInfo *exception)
1188 % A description of each parameter follows:
1190 % o image: the image.
1192 % o minima: the minimum value in the channel.
1194 % o maxima: the maximum value in the channel.
1196 % o exception: return any errors or warnings in this structure.
1199 MagickExport MagickBooleanType GetImageExtrema(const Image *image,
1200 size_t *minima,size_t *maxima,ExceptionInfo *exception)
1209 assert(image != (Image *) NULL);
1210 assert(image->signature == MagickCoreSignature);
1211 if (image->debug != MagickFalse)
1212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1213 status=GetImageRange(image,&min,&max,exception);
1214 *minima=(size_t) ceil(min-0.5);
1215 *maxima=(size_t) floor(max+0.5);
1220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1224 % G e t I m a g e K u r t o s i s %
1228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230 % GetImageKurtosis() returns the kurtosis and skewness of one or more image
1233 % The format of the GetImageKurtosis method is:
1235 % MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1236 % double *skewness,ExceptionInfo *exception)
1238 % A description of each parameter follows:
1240 % o image: the image.
1242 % o kurtosis: the kurtosis of the channel.
1244 % o skewness: the skewness of the channel.
1246 % o exception: return any errors or warnings in this structure.
1249 MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1250 double *kurtosis,double *skewness,ExceptionInfo *exception)
1253 *channel_statistics;
1255 assert(image != (Image *) NULL);
1256 assert(image->signature == MagickCoreSignature);
1257 if (image->debug != MagickFalse)
1258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1259 channel_statistics=GetImageStatistics(image,exception);
1260 if (channel_statistics == (ChannelStatistics *) NULL)
1261 return(MagickFalse);
1262 *kurtosis=channel_statistics[CompositePixelChannel].kurtosis;
1263 *skewness=channel_statistics[CompositePixelChannel].skewness;
1264 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1265 channel_statistics);
1270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1274 % G e t I m a g e M e a n %
1278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280 % GetImageMean() returns the mean and standard deviation of one or more image
1283 % The format of the GetImageMean method is:
1285 % MagickBooleanType GetImageMean(const Image *image,double *mean,
1286 % double *standard_deviation,ExceptionInfo *exception)
1288 % A description of each parameter follows:
1290 % o image: the image.
1292 % o mean: the average value in the channel.
1294 % o standard_deviation: the standard deviation of the channel.
1296 % o exception: return any errors or warnings in this structure.
1299 MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1300 double *standard_deviation,ExceptionInfo *exception)
1303 *channel_statistics;
1305 assert(image != (Image *) NULL);
1306 assert(image->signature == MagickCoreSignature);
1307 if (image->debug != MagickFalse)
1308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1309 channel_statistics=GetImageStatistics(image,exception);
1310 if (channel_statistics == (ChannelStatistics *) NULL)
1311 return(MagickFalse);
1312 *mean=channel_statistics[CompositePixelChannel].mean;
1313 *standard_deviation=
1314 channel_statistics[CompositePixelChannel].standard_deviation;
1315 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1316 channel_statistics);
1321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1325 % G e t I m a g e M o m e n t s %
1329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1331 % GetImageMoments() returns the normalized moments of one or more image
1334 % The format of the GetImageMoments method is:
1336 % ChannelMoments *GetImageMoments(const Image *image,
1337 % ExceptionInfo *exception)
1339 % A description of each parameter follows:
1341 % o image: the image.
1343 % o exception: return any errors or warnings in this structure.
1347 static size_t GetImageChannels(const Image *image)
1356 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1358 PixelChannel channel = GetPixelChannelChannel(image,i);
1359 PixelTrait traits = GetPixelChannelTraits(image,channel);
1360 if (traits == UndefinedPixelTrait)
1362 if ((traits & UpdatePixelTrait) == 0)
1366 return((size_t) (channels == 0 ? 1 : channels));
1369 MagickExport ChannelMoments *GetImageMoments(const Image *image,
1370 ExceptionInfo *exception)
1372 #define MaxNumberImageMoments 8
1381 M00[MaxPixelChannels+1],
1382 M01[MaxPixelChannels+1],
1383 M02[MaxPixelChannels+1],
1384 M03[MaxPixelChannels+1],
1385 M10[MaxPixelChannels+1],
1386 M11[MaxPixelChannels+1],
1387 M12[MaxPixelChannels+1],
1388 M20[MaxPixelChannels+1],
1389 M21[MaxPixelChannels+1],
1390 M22[MaxPixelChannels+1],
1391 M30[MaxPixelChannels+1];
1394 centroid[MaxPixelChannels+1];
1400 assert(image != (Image *) NULL);
1401 assert(image->signature == MagickCoreSignature);
1402 if (image->debug != MagickFalse)
1403 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1404 channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1405 sizeof(*channel_moments));
1406 if (channel_moments == (ChannelMoments *) NULL)
1407 return(channel_moments);
1408 (void) memset(channel_moments,0,(MaxPixelChannels+1)*
1409 sizeof(*channel_moments));
1410 (void) memset(centroid,0,sizeof(centroid));
1411 (void) memset(M00,0,sizeof(M00));
1412 (void) memset(M01,0,sizeof(M01));
1413 (void) memset(M02,0,sizeof(M02));
1414 (void) memset(M03,0,sizeof(M03));
1415 (void) memset(M10,0,sizeof(M10));
1416 (void) memset(M11,0,sizeof(M11));
1417 (void) memset(M12,0,sizeof(M12));
1418 (void) memset(M20,0,sizeof(M20));
1419 (void) memset(M21,0,sizeof(M21));
1420 (void) memset(M22,0,sizeof(M22));
1421 (void) memset(M30,0,sizeof(M30));
1422 image_view=AcquireVirtualCacheView(image,exception);
1423 for (y=0; y < (ssize_t) image->rows; y++)
1425 register const Quantum
1432 Compute center of mass (centroid).
1434 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1435 if (p == (const Quantum *) NULL)
1437 for (x=0; x < (ssize_t) image->columns; x++)
1442 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1444 PixelChannel channel = GetPixelChannelChannel(image,i);
1445 PixelTrait traits = GetPixelChannelTraits(image,channel);
1446 if (traits == UndefinedPixelTrait)
1448 if ((traits & UpdatePixelTrait) == 0)
1450 M00[channel]+=QuantumScale*p[i];
1451 M00[MaxPixelChannels]+=QuantumScale*p[i];
1452 M10[channel]+=x*QuantumScale*p[i];
1453 M10[MaxPixelChannels]+=x*QuantumScale*p[i];
1454 M01[channel]+=y*QuantumScale*p[i];
1455 M01[MaxPixelChannels]+=y*QuantumScale*p[i];
1457 p+=GetPixelChannels(image);
1460 for (channel=0; channel <= MaxPixelChannels; channel++)
1463 Compute center of mass (centroid).
1465 if (M00[channel] < MagickEpsilon)
1467 M00[channel]+=MagickEpsilon;
1468 centroid[channel].x=(double) image->columns/2.0;
1469 centroid[channel].y=(double) image->rows/2.0;
1472 M00[channel]+=MagickEpsilon;
1473 centroid[channel].x=M10[channel]/M00[channel];
1474 centroid[channel].y=M01[channel]/M00[channel];
1476 for (y=0; y < (ssize_t) image->rows; y++)
1478 register const Quantum
1485 Compute the image moments.
1487 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1488 if (p == (const Quantum *) NULL)
1490 for (x=0; x < (ssize_t) image->columns; x++)
1495 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1497 PixelChannel channel = GetPixelChannelChannel(image,i);
1498 PixelTrait traits = GetPixelChannelTraits(image,channel);
1499 if (traits == UndefinedPixelTrait)
1501 if ((traits & UpdatePixelTrait) == 0)
1503 M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1505 M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1507 M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1509 M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1511 M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1513 M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1515 M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1516 (y-centroid[channel].y)*QuantumScale*p[i];
1517 M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1518 (y-centroid[channel].y)*QuantumScale*p[i];
1519 M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1520 (y-centroid[channel].y)*QuantumScale*p[i];
1521 M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1522 (y-centroid[channel].y)*QuantumScale*p[i];
1523 M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1524 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1525 M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1526 (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1527 M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1528 (x-centroid[channel].x)*QuantumScale*p[i];
1529 M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1530 (x-centroid[channel].x)*QuantumScale*p[i];
1531 M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1532 (y-centroid[channel].y)*QuantumScale*p[i];
1533 M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1534 (y-centroid[channel].y)*QuantumScale*p[i];
1536 p+=GetPixelChannels(image);
1539 M00[MaxPixelChannels]/=GetImageChannels(image);
1540 M01[MaxPixelChannels]/=GetImageChannels(image);
1541 M02[MaxPixelChannels]/=GetImageChannels(image);
1542 M03[MaxPixelChannels]/=GetImageChannels(image);
1543 M10[MaxPixelChannels]/=GetImageChannels(image);
1544 M11[MaxPixelChannels]/=GetImageChannels(image);
1545 M12[MaxPixelChannels]/=GetImageChannels(image);
1546 M20[MaxPixelChannels]/=GetImageChannels(image);
1547 M21[MaxPixelChannels]/=GetImageChannels(image);
1548 M22[MaxPixelChannels]/=GetImageChannels(image);
1549 M30[MaxPixelChannels]/=GetImageChannels(image);
1550 for (channel=0; channel <= MaxPixelChannels; channel++)
1553 Compute elliptical angle, major and minor axes, eccentricity, & intensity.
1555 channel_moments[channel].centroid=centroid[channel];
1556 channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
1557 ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
1558 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1559 channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
1560 ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
1561 (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1562 channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
1563 M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
1564 if (fabs(M11[channel]) < MagickEpsilon)
1566 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1567 channel_moments[channel].ellipse_angle+=0.0;
1569 if ((M20[channel]-M02[channel]) < 0.0)
1570 channel_moments[channel].ellipse_angle+=90.0;
1572 channel_moments[channel].ellipse_angle+=0.0;
1575 if (M11[channel] < 0.0)
1577 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1578 channel_moments[channel].ellipse_angle+=0.0;
1580 if ((M20[channel]-M02[channel]) < 0.0)
1581 channel_moments[channel].ellipse_angle+=90.0;
1583 channel_moments[channel].ellipse_angle+=180.0;
1587 if (fabs(M20[channel]-M02[channel]) < MagickEpsilon)
1588 channel_moments[channel].ellipse_angle+=0.0;
1590 if ((M20[channel]-M02[channel]) < 0.0)
1591 channel_moments[channel].ellipse_angle+=90.0;
1593 channel_moments[channel].ellipse_angle+=0.0;
1595 channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
1596 channel_moments[channel].ellipse_axis.y/
1597 (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
1598 channel_moments[channel].ellipse_intensity=M00[channel]/
1599 (MagickPI*channel_moments[channel].ellipse_axis.x*
1600 channel_moments[channel].ellipse_axis.y+MagickEpsilon);
1602 for (channel=0; channel <= MaxPixelChannels; channel++)
1605 Normalize image moments.
1609 M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
1610 M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
1611 M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
1612 M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
1613 M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
1614 M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
1615 M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
1616 M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
1619 image_view=DestroyCacheView(image_view);
1620 for (channel=0; channel <= MaxPixelChannels; channel++)
1623 Compute Hu invariant moments.
1625 channel_moments[channel].invariant[0]=M20[channel]+M02[channel];
1626 channel_moments[channel].invariant[1]=(M20[channel]-M02[channel])*
1627 (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
1628 channel_moments[channel].invariant[2]=(M30[channel]-3.0*M12[channel])*
1629 (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
1630 (3.0*M21[channel]-M03[channel]);
1631 channel_moments[channel].invariant[3]=(M30[channel]+M12[channel])*
1632 (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
1633 (M21[channel]+M03[channel]);
1634 channel_moments[channel].invariant[4]=(M30[channel]-3.0*M12[channel])*
1635 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1636 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1637 (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
1638 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1639 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1640 (M21[channel]+M03[channel]));
1641 channel_moments[channel].invariant[5]=(M20[channel]-M02[channel])*
1642 ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
1643 (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
1644 4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
1645 channel_moments[channel].invariant[6]=(3.0*M21[channel]-M03[channel])*
1646 (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1647 (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1648 (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
1649 (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1650 (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1651 (M21[channel]+M03[channel]));
1652 channel_moments[channel].invariant[7]=M11[channel]*((M30[channel]+
1653 M12[channel])*(M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
1654 (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
1655 (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
1657 if (y < (ssize_t) image->rows)
1658 channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1659 return(channel_moments);
1663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1667 % 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 %
1671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1673 % GetImagePerceptualHash() returns the perceptual hash of one or more
1676 % The format of the GetImagePerceptualHash method is:
1678 % ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1679 % ExceptionInfo *exception)
1681 % A description of each parameter follows:
1683 % o image: the image.
1685 % o exception: return any errors or warnings in this structure.
1689 static inline double MagickLog10(const double x)
1691 #define Log10Epsilon (1.0e-11)
1693 if (fabs(x) < Log10Epsilon)
1694 return(log10(Log10Epsilon));
1695 return(log10(fabs(x)));
1698 MagickExport ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1699 ExceptionInfo *exception)
1701 ChannelPerceptualHash
1720 perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1721 MaxPixelChannels+1UL,sizeof(*perceptual_hash));
1722 if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1723 return((ChannelPerceptualHash *) NULL);
1724 artifact=GetImageArtifact(image,"phash:colorspaces");
1725 if (artifact != NULL)
1726 colorspaces=AcquireString(artifact);
1728 colorspaces=AcquireString("sRGB,HCLp");
1729 perceptual_hash[0].number_colorspaces=0;
1730 perceptual_hash[0].number_channels=0;
1732 for (i=0; (p=StringToken(",",&q)) != (char *) NULL; i++)
1747 if (i >= MaximumNumberOfPerceptualColorspaces)
1749 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,p);
1752 perceptual_hash[0].colorspace[i]=(ColorspaceType) colorspace;
1753 hash_image=BlurImage(image,0.0,1.0,exception);
1754 if (hash_image == (Image *) NULL)
1756 hash_image->depth=8;
1757 status=TransformImageColorspace(hash_image,(ColorspaceType) colorspace,
1759 if (status == MagickFalse)
1761 moments=GetImageMoments(hash_image,exception);
1762 perceptual_hash[0].number_colorspaces++;
1763 perceptual_hash[0].number_channels+=GetImageChannels(hash_image);
1764 hash_image=DestroyImage(hash_image);
1765 if (moments == (ChannelMoments *) NULL)
1767 for (channel=0; channel <= MaxPixelChannels; channel++)
1768 for (j=0; j < MaximumNumberOfImageMoments; j++)
1769 perceptual_hash[channel].phash[i][j]=
1770 (-MagickLog10(moments[channel].invariant[j]));
1771 moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1773 colorspaces=DestroyString(colorspaces);
1774 return(perceptual_hash);
1778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782 % G e t I m a g e R a n g e %
1786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1788 % GetImageRange() returns the range of one or more image channels.
1790 % The format of the GetImageRange method is:
1792 % MagickBooleanType GetImageRange(const Image *image,double *minima,
1793 % double *maxima,ExceptionInfo *exception)
1795 % A description of each parameter follows:
1797 % o image: the image.
1799 % o minima: the minimum value in the channel.
1801 % o maxima: the maximum value in the channel.
1803 % o exception: return any errors or warnings in this structure.
1806 MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1807 double *maxima,ExceptionInfo *exception)
1819 assert(image != (Image *) NULL);
1820 assert(image->signature == MagickCoreSignature);
1821 if (image->debug != MagickFalse)
1822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1824 initialize=MagickTrue;
1827 image_view=AcquireVirtualCacheView(image,exception);
1828 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1829 #pragma omp parallel for schedule(static) shared(status,initialize) \
1830 magick_number_threads(image,image,image->rows,1)
1832 for (y=0; y < (ssize_t) image->rows; y++)
1841 register const Quantum
1847 if (status == MagickFalse)
1849 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1850 if (p == (const Quantum *) NULL)
1855 row_initialize=MagickTrue;
1856 for (x=0; x < (ssize_t) image->columns; x++)
1861 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1863 PixelChannel channel = GetPixelChannelChannel(image,i);
1864 PixelTrait traits = GetPixelChannelTraits(image,channel);
1865 if (traits == UndefinedPixelTrait)
1867 if ((traits & UpdatePixelTrait) == 0)
1869 if (row_initialize != MagickFalse)
1871 row_minima=(double) p[i];
1872 row_maxima=(double) p[i];
1873 row_initialize=MagickFalse;
1877 if ((double) p[i] < row_minima)
1878 row_minima=(double) p[i];
1879 if ((double) p[i] > row_maxima)
1880 row_maxima=(double) p[i];
1883 p+=GetPixelChannels(image);
1885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1886 #pragma omp critical (MagickCore_GetImageRange)
1889 if (initialize != MagickFalse)
1893 initialize=MagickFalse;
1897 if (row_minima < *minima)
1899 if (row_maxima > *maxima)
1904 image_view=DestroyCacheView(image_view);
1909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1913 % G e t I m a g e S t a t i s t i c s %
1917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1919 % GetImageStatistics() returns statistics for each channel in the image. The
1920 % statistics include the channel depth, its minima, maxima, mean, standard
1921 % deviation, kurtosis and skewness. You can access the red channel mean, for
1922 % example, like this:
1924 % channel_statistics=GetImageStatistics(image,exception);
1925 % red_mean=channel_statistics[RedPixelChannel].mean;
1927 % Use MagickRelinquishMemory() to free the statistics buffer.
1929 % The format of the GetImageStatistics method is:
1931 % ChannelStatistics *GetImageStatistics(const Image *image,
1932 % ExceptionInfo *exception)
1934 % A description of each parameter follows:
1936 % o image: the image.
1938 % o exception: return any errors or warnings in this structure.
1941 MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
1942 ExceptionInfo *exception)
1945 *channel_statistics;
1967 assert(image != (Image *) NULL);
1968 assert(image->signature == MagickCoreSignature);
1969 if (image->debug != MagickFalse)
1970 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1971 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1972 sizeof(*histogram));
1973 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1974 MaxPixelChannels+1,sizeof(*channel_statistics));
1975 if ((channel_statistics == (ChannelStatistics *) NULL) ||
1976 (histogram == (double *) NULL))
1978 if (histogram != (double *) NULL)
1979 histogram=(double *) RelinquishMagickMemory(histogram);
1980 if (channel_statistics != (ChannelStatistics *) NULL)
1981 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1982 channel_statistics);
1983 return(channel_statistics);
1985 (void) memset(channel_statistics,0,(MaxPixelChannels+1)*
1986 sizeof(*channel_statistics));
1987 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1989 channel_statistics[i].depth=1;
1990 channel_statistics[i].maxima=(-MagickMaximumValue);
1991 channel_statistics[i].minima=MagickMaximumValue;
1993 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1994 sizeof(*histogram));
1995 for (y=0; y < (ssize_t) image->rows; y++)
1997 register const Quantum
2004 Compute pixel statistics.
2006 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2007 if (p == (const Quantum *) NULL)
2009 for (x=0; x < (ssize_t) image->columns; x++)
2014 if (GetPixelReadMask(image,p) <= (QuantumRange/2))
2016 p+=GetPixelChannels(image);
2019 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2021 PixelChannel channel = GetPixelChannelChannel(image,i);
2022 PixelTrait traits = GetPixelChannelTraits(image,channel);
2023 if (traits == UndefinedPixelTrait)
2025 if ((traits & UpdatePixelTrait) == 0)
2027 if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
2029 depth=channel_statistics[channel].depth;
2030 range=GetQuantumRange(depth);
2031 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2032 range) ? MagickTrue : MagickFalse;
2033 if (status != MagickFalse)
2035 channel_statistics[channel].depth++;
2040 if ((double) p[i] < channel_statistics[channel].minima)
2041 channel_statistics[channel].minima=(double) p[i];
2042 if ((double) p[i] > channel_statistics[channel].maxima)
2043 channel_statistics[channel].maxima=(double) p[i];
2044 channel_statistics[channel].sum+=p[i];
2045 channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
2046 channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
2047 channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
2049 channel_statistics[channel].area++;
2050 if ((double) p[i] < channel_statistics[CompositePixelChannel].minima)
2051 channel_statistics[CompositePixelChannel].minima=(double) p[i];
2052 if ((double) p[i] > channel_statistics[CompositePixelChannel].maxima)
2053 channel_statistics[CompositePixelChannel].maxima=(double) p[i];
2054 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2055 ClampToQuantum((double) p[i]))+i]++;
2056 channel_statistics[CompositePixelChannel].sum+=(double) p[i];
2057 channel_statistics[CompositePixelChannel].sum_squared+=(double)
2059 channel_statistics[CompositePixelChannel].sum_cubed+=(double)
2061 channel_statistics[CompositePixelChannel].sum_fourth_power+=(double)
2062 p[i]*p[i]*p[i]*p[i];
2063 channel_statistics[CompositePixelChannel].area++;
2065 p+=GetPixelChannels(image);
2068 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2071 Normalize pixel statistics.
2073 area=PerceptibleReciprocal(channel_statistics[i].area);
2074 channel_statistics[i].sum*=area;
2075 channel_statistics[i].sum_squared*=area;
2076 channel_statistics[i].sum_cubed*=area;
2077 channel_statistics[i].sum_fourth_power*=area;
2078 channel_statistics[i].mean=channel_statistics[i].sum;
2079 channel_statistics[i].variance=channel_statistics[i].sum_squared;
2080 standard_deviation=sqrt(channel_statistics[i].variance-
2081 (channel_statistics[i].mean*channel_statistics[i].mean));
2082 standard_deviation=sqrt(PerceptibleReciprocal(channel_statistics[i].area-
2083 1.0)*channel_statistics[i].area*standard_deviation*standard_deviation);
2084 channel_statistics[i].standard_deviation=standard_deviation;
2086 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2095 Compute pixel entropy.
2097 PixelChannel channel = GetPixelChannelChannel(image,i);
2099 for (j=0; j <= (ssize_t) MaxMap; j++)
2100 if (histogram[GetPixelChannels(image)*j+i] > 0.0)
2102 area=PerceptibleReciprocal(channel_statistics[channel].area);
2103 for (j=0; j <= (ssize_t) MaxMap; j++)
2108 count=area*histogram[GetPixelChannels(image)*j+i];
2109 channel_statistics[channel].entropy+=-count*MagickLog10(count)*
2110 PerceptibleReciprocal(MagickLog10(number_bins));
2111 channel_statistics[CompositePixelChannel].entropy+=-count*
2112 MagickLog10(count)*PerceptibleReciprocal(MagickLog10(number_bins))/
2113 GetPixelChannels(image);
2116 histogram=(double *) RelinquishMagickMemory(histogram);
2117 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2120 Compute kurtosis & skewness statistics.
2122 standard_deviation=PerceptibleReciprocal(
2123 channel_statistics[i].standard_deviation);
2124 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
2125 channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
2126 channel_statistics[i].mean*channel_statistics[i].mean*
2127 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2128 standard_deviation);
2129 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
2130 channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
2131 channel_statistics[i].mean*channel_statistics[i].mean*
2132 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
2133 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
2134 channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2135 standard_deviation*standard_deviation)-3.0;
2137 channel_statistics[CompositePixelChannel].mean=0.0;
2138 channel_statistics[CompositePixelChannel].standard_deviation=0.0;
2139 channel_statistics[CompositePixelChannel].entropy=0.0;
2140 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2142 channel_statistics[CompositePixelChannel].mean+=
2143 channel_statistics[i].mean;
2144 channel_statistics[CompositePixelChannel].standard_deviation+=
2145 channel_statistics[i].standard_deviation;
2146 channel_statistics[CompositePixelChannel].entropy+=
2147 channel_statistics[i].entropy;
2149 channel_statistics[CompositePixelChannel].mean/=(double)
2150 GetImageChannels(image);
2151 channel_statistics[CompositePixelChannel].standard_deviation/=(double)
2152 GetImageChannels(image);
2153 channel_statistics[CompositePixelChannel].entropy/=(double)
2154 GetImageChannels(image);
2155 if (y < (ssize_t) image->rows)
2156 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2157 channel_statistics);
2158 return(channel_statistics);
2162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2166 % P o l y n o m i a l I m a g e %
2170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2172 % PolynomialImage() returns a new image where each pixel is the sum of the
2173 % pixels in the image sequence after applying its corresponding terms
2174 % (coefficient and degree pairs).
2176 % The format of the PolynomialImage method is:
2178 % Image *PolynomialImage(const Image *images,const size_t number_terms,
2179 % const double *terms,ExceptionInfo *exception)
2181 % A description of each parameter follows:
2183 % o images: the image sequence.
2185 % o number_terms: the number of terms in the list. The actual list length
2186 % is 2 x number_terms + 1 (the constant).
2188 % o terms: the list of polynomial coefficients and degree pairs and a
2191 % o exception: return any errors or warnings in this structure.
2194 MagickExport Image *PolynomialImage(const Image *images,
2195 const size_t number_terms,const double *terms,ExceptionInfo *exception)
2197 #define PolynomialImageTag "Polynomial/Image"
2212 **magick_restrict polynomial_pixels;
2220 assert(images != (Image *) NULL);
2221 assert(images->signature == MagickCoreSignature);
2222 if (images->debug != MagickFalse)
2223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2224 assert(exception != (ExceptionInfo *) NULL);
2225 assert(exception->signature == MagickCoreSignature);
2226 image=AcquireImageCanvas(images,exception);
2227 if (image == (Image *) NULL)
2228 return((Image *) NULL);
2229 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2231 image=DestroyImage(image);
2232 return((Image *) NULL);
2234 number_images=GetImageListLength(images);
2235 polynomial_pixels=AcquirePixelThreadSet(images);
2236 if (polynomial_pixels == (PixelChannels **) NULL)
2238 image=DestroyImage(image);
2239 (void) ThrowMagickException(exception,GetMagickModule(),
2240 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2241 return((Image *) NULL);
2244 Polynomial image pixels.
2248 polynomial_view=AcquireAuthenticCacheView(image,exception);
2249 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2250 #pragma omp parallel for schedule(static) shared(progress,status) \
2251 magick_number_threads(image,image,image->rows,1)
2253 for (y=0; y < (ssize_t) image->rows; y++)
2262 id = GetOpenMPThreadId();
2268 register PixelChannels
2277 if (status == MagickFalse)
2279 q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2281 if (q == (Quantum *) NULL)
2286 polynomial_pixel=polynomial_pixels[id];
2287 for (j=0; j < (ssize_t) image->columns; j++)
2288 for (i=0; i < MaxPixelChannels; i++)
2289 polynomial_pixel[j].channel[i]=0.0;
2291 for (j=0; j < (ssize_t) number_images; j++)
2293 register const Quantum
2296 if (j >= (ssize_t) number_terms)
2298 image_view=AcquireVirtualCacheView(next,exception);
2299 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2300 if (p == (const Quantum *) NULL)
2302 image_view=DestroyCacheView(image_view);
2305 for (x=0; x < (ssize_t) image->columns; x++)
2310 for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2316 PixelChannel channel = GetPixelChannelChannel(image,i);
2317 PixelTrait traits = GetPixelChannelTraits(next,channel);
2318 PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
2319 if ((traits == UndefinedPixelTrait) ||
2320 (polynomial_traits == UndefinedPixelTrait))
2322 if ((traits & UpdatePixelTrait) == 0)
2324 coefficient=(MagickRealType) terms[2*j];
2325 degree=(MagickRealType) terms[(j << 1)+1];
2326 polynomial_pixel[x].channel[i]+=coefficient*
2327 pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
2329 p+=GetPixelChannels(next);
2331 image_view=DestroyCacheView(image_view);
2332 next=GetNextImageInList(next);
2334 for (x=0; x < (ssize_t) image->columns; x++)
2339 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2341 PixelChannel channel = GetPixelChannelChannel(image,i);
2342 PixelTrait traits = GetPixelChannelTraits(image,channel);
2343 if (traits == UndefinedPixelTrait)
2345 if ((traits & UpdatePixelTrait) == 0)
2347 q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
2349 q+=GetPixelChannels(image);
2351 if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2353 if (images->progress_monitor != (MagickProgressMonitor) NULL)
2358 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2362 proceed=SetImageProgress(images,PolynomialImageTag,progress,
2364 if (proceed == MagickFalse)
2368 polynomial_view=DestroyCacheView(polynomial_view);
2369 polynomial_pixels=DestroyPixelThreadSet(images,polynomial_pixels);
2370 if (status == MagickFalse)
2371 image=DestroyImage(image);
2376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2380 % S t a t i s t i c I m a g e %
2384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2386 % StatisticImage() makes each pixel the min / max / median / mode / etc. of
2387 % the neighborhood of the specified width and height.
2389 % The format of the StatisticImage method is:
2391 % Image *StatisticImage(const Image *image,const StatisticType type,
2392 % const size_t width,const size_t height,ExceptionInfo *exception)
2394 % A description of each parameter follows:
2396 % o image: the image.
2398 % o type: the statistic type (median, mode, etc.).
2400 % o width: the width of the pixel neighborhood.
2402 % o height: the height of the pixel neighborhood.
2404 % o exception: return any errors or warnings in this structure.
2408 typedef struct _SkipNode
2416 typedef struct _SkipList
2425 typedef struct _PixelList
2438 static PixelList *DestroyPixelList(PixelList *pixel_list)
2440 if (pixel_list == (PixelList *) NULL)
2441 return((PixelList *) NULL);
2442 if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2443 pixel_list->skip_list.nodes=(SkipNode *) RelinquishAlignedMemory(
2444 pixel_list->skip_list.nodes);
2445 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2449 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2454 assert(pixel_list != (PixelList **) NULL);
2455 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2456 if (pixel_list[i] != (PixelList *) NULL)
2457 pixel_list[i]=DestroyPixelList(pixel_list[i]);
2458 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2462 static PixelList *AcquirePixelList(const size_t width,const size_t height)
2467 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2468 if (pixel_list == (PixelList *) NULL)
2470 (void) memset((void *) pixel_list,0,sizeof(*pixel_list));
2471 pixel_list->length=width*height;
2472 pixel_list->skip_list.nodes=(SkipNode *) AcquireAlignedMemory(65537UL,
2473 sizeof(*pixel_list->skip_list.nodes));
2474 if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2475 return(DestroyPixelList(pixel_list));
2476 (void) memset(pixel_list->skip_list.nodes,0,65537UL*
2477 sizeof(*pixel_list->skip_list.nodes));
2478 pixel_list->signature=MagickCoreSignature;
2482 static PixelList **AcquirePixelListThreadSet(const size_t width,
2483 const size_t height)
2494 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2495 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2496 sizeof(*pixel_list));
2497 if (pixel_list == (PixelList **) NULL)
2498 return((PixelList **) NULL);
2499 (void) memset(pixel_list,0,number_threads*sizeof(*pixel_list));
2500 for (i=0; i < (ssize_t) number_threads; i++)
2502 pixel_list[i]=AcquirePixelList(width,height);
2503 if (pixel_list[i] == (PixelList *) NULL)
2504 return(DestroyPixelListThreadSet(pixel_list));
2509 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
2522 Initialize the node.
2524 p=(&pixel_list->skip_list);
2525 p->nodes[color].signature=pixel_list->signature;
2526 p->nodes[color].count=1;
2528 Determine where it belongs in the list.
2531 for (level=p->level; level >= 0; level--)
2533 while (p->nodes[search].next[level] < color)
2534 search=p->nodes[search].next[level];
2535 update[level]=search;
2538 Generate a pseudo-random level for this node.
2540 for (level=0; ; level++)
2542 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2543 if ((pixel_list->seed & 0x300) != 0x300)
2548 if (level > (p->level+2))
2551 If we're raising the list's level, link back to the root node.
2553 while (level > p->level)
2556 update[p->level]=65536UL;
2559 Link the node into the skip-list.
2563 p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2564 p->nodes[update[level]].next[level]=color;
2565 } while (level-- > 0);
2568 static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
2581 Find the maximum value for each of the color.
2583 p=(&pixel_list->skip_list);
2586 maximum=p->nodes[color].next[0];
2589 color=p->nodes[color].next[0];
2590 if (color > maximum)
2592 count+=p->nodes[color].count;
2593 } while (count < (ssize_t) pixel_list->length);
2594 *pixel=ScaleShortToQuantum((unsigned short) maximum);
2597 static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
2612 Find the mean value for each of the color.
2614 p=(&pixel_list->skip_list);
2620 color=p->nodes[color].next[0];
2621 sum+=(double) p->nodes[color].count*color;
2622 count+=p->nodes[color].count;
2623 } while (count < (ssize_t) pixel_list->length);
2624 sum/=pixel_list->length;
2625 *pixel=ScaleShortToQuantum((unsigned short) sum);
2628 static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2640 Find the median value for each of the color.
2642 p=(&pixel_list->skip_list);
2647 color=p->nodes[color].next[0];
2648 count+=p->nodes[color].count;
2649 } while (count <= (ssize_t) (pixel_list->length >> 1));
2650 *pixel=ScaleShortToQuantum((unsigned short) color);
2653 static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
2666 Find the minimum value for each of the color.
2668 p=(&pixel_list->skip_list);
2671 minimum=p->nodes[color].next[0];
2674 color=p->nodes[color].next[0];
2675 if (color < minimum)
2677 count+=p->nodes[color].count;
2678 } while (count < (ssize_t) pixel_list->length);
2679 *pixel=ScaleShortToQuantum((unsigned short) minimum);
2682 static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2696 Make each pixel the 'predominant color' of the specified neighborhood.
2698 p=(&pixel_list->skip_list);
2701 max_count=p->nodes[mode].count;
2705 color=p->nodes[color].next[0];
2706 if (p->nodes[color].count > max_count)
2709 max_count=p->nodes[mode].count;
2711 count+=p->nodes[color].count;
2712 } while (count < (ssize_t) pixel_list->length);
2713 *pixel=ScaleShortToQuantum((unsigned short) mode);
2716 static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2730 Finds the non peak value for each of the colors.
2732 p=(&pixel_list->skip_list);
2734 next=p->nodes[color].next[0];
2740 next=p->nodes[color].next[0];
2741 count+=p->nodes[color].count;
2742 } while (count <= (ssize_t) (pixel_list->length >> 1));
2743 if ((previous == 65536UL) && (next != 65536UL))
2746 if ((previous != 65536UL) && (next == 65536UL))
2748 *pixel=ScaleShortToQuantum((unsigned short) color);
2751 static inline void GetRootMeanSquarePixelList(PixelList *pixel_list,
2767 Find the root mean square value for each of the color.
2769 p=(&pixel_list->skip_list);
2775 color=p->nodes[color].next[0];
2776 sum+=(double) (p->nodes[color].count*color*color);
2777 count+=p->nodes[color].count;
2778 } while (count < (ssize_t) pixel_list->length);
2779 sum/=pixel_list->length;
2780 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum));
2783 static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2800 Find the standard-deviation value for each of the color.
2802 p=(&pixel_list->skip_list);
2812 color=p->nodes[color].next[0];
2813 sum+=(double) p->nodes[color].count*color;
2814 for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2815 sum_squared+=((double) color)*((double) color);
2816 count+=p->nodes[color].count;
2817 } while (count < (ssize_t) pixel_list->length);
2818 sum/=pixel_list->length;
2819 sum_squared/=pixel_list->length;
2820 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2823 static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
2831 index=ScaleQuantumToShort(pixel);
2832 signature=pixel_list->skip_list.nodes[index].signature;
2833 if (signature == pixel_list->signature)
2835 pixel_list->skip_list.nodes[index].count++;
2838 AddNodePixelList(pixel_list,index);
2841 static void ResetPixelList(PixelList *pixel_list)
2853 Reset the skip-list.
2855 p=(&pixel_list->skip_list);
2856 root=p->nodes+65536UL;
2858 for (level=0; level < 9; level++)
2859 root->next[level]=65536UL;
2860 pixel_list->seed=pixel_list->signature++;
2863 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2864 const size_t width,const size_t height,ExceptionInfo *exception)
2866 #define StatisticImageTag "Statistic/Image"
2882 **magick_restrict pixel_list;
2889 Initialize statistics image attributes.
2891 assert(image != (Image *) NULL);
2892 assert(image->signature == MagickCoreSignature);
2893 if (image->debug != MagickFalse)
2894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2895 assert(exception != (ExceptionInfo *) NULL);
2896 assert(exception->signature == MagickCoreSignature);
2897 statistic_image=CloneImage(image,0,0,MagickTrue,
2899 if (statistic_image == (Image *) NULL)
2900 return((Image *) NULL);
2901 status=SetImageStorageClass(statistic_image,DirectClass,exception);
2902 if (status == MagickFalse)
2904 statistic_image=DestroyImage(statistic_image);
2905 return((Image *) NULL);
2907 pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
2908 if (pixel_list == (PixelList **) NULL)
2910 statistic_image=DestroyImage(statistic_image);
2911 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2914 Make each pixel the min / max / median / mode / etc. of the neighborhood.
2916 center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
2917 (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
2920 image_view=AcquireVirtualCacheView(image,exception);
2921 statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
2922 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2923 #pragma omp parallel for schedule(static) shared(progress,status) \
2924 magick_number_threads(image,statistic_image,statistic_image->rows,1)
2926 for (y=0; y < (ssize_t) statistic_image->rows; y++)
2929 id = GetOpenMPThreadId();
2931 register const Quantum
2940 if (status == MagickFalse)
2942 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
2943 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
2944 MagickMax(height,1),exception);
2945 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
2946 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2951 for (x=0; x < (ssize_t) statistic_image->columns; x++)
2956 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2961 register const Quantum
2962 *magick_restrict pixels;
2970 PixelChannel channel = GetPixelChannelChannel(image,i);
2971 PixelTrait traits = GetPixelChannelTraits(image,channel);
2972 PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
2974 if ((traits == UndefinedPixelTrait) ||
2975 (statistic_traits == UndefinedPixelTrait))
2977 if (((statistic_traits & CopyPixelTrait) != 0) ||
2978 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
2980 SetPixelChannel(statistic_image,channel,p[center+i],q);
2983 if ((statistic_traits & UpdatePixelTrait) == 0)
2986 ResetPixelList(pixel_list[id]);
2987 for (v=0; v < (ssize_t) MagickMax(height,1); v++)
2989 for (u=0; u < (ssize_t) MagickMax(width,1); u++)
2991 InsertPixelList(pixels[i],pixel_list[id]);
2992 pixels+=GetPixelChannels(image);
2994 pixels+=GetPixelChannels(image)*image->columns;
2998 case GradientStatistic:
3004 GetMinimumPixelList(pixel_list[id],&pixel);
3005 minimum=(double) pixel;
3006 GetMaximumPixelList(pixel_list[id],&pixel);
3007 maximum=(double) pixel;
3008 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
3011 case MaximumStatistic:
3013 GetMaximumPixelList(pixel_list[id],&pixel);
3018 GetMeanPixelList(pixel_list[id],&pixel);
3021 case MedianStatistic:
3024 GetMedianPixelList(pixel_list[id],&pixel);
3027 case MinimumStatistic:
3029 GetMinimumPixelList(pixel_list[id],&pixel);
3034 GetModePixelList(pixel_list[id],&pixel);
3037 case NonpeakStatistic:
3039 GetNonpeakPixelList(pixel_list[id],&pixel);
3042 case RootMeanSquareStatistic:
3044 GetRootMeanSquarePixelList(pixel_list[id],&pixel);
3047 case StandardDeviationStatistic:
3049 GetStandardDeviationPixelList(pixel_list[id],&pixel);
3053 SetPixelChannel(statistic_image,channel,pixel,q);
3055 p+=GetPixelChannels(image);
3056 q+=GetPixelChannels(statistic_image);
3058 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3060 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3065 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3069 proceed=SetImageProgress(image,StatisticImageTag,progress,image->rows);
3070 if (proceed == MagickFalse)
3074 statistic_view=DestroyCacheView(statistic_view);
3075 image_view=DestroyCacheView(image_view);
3076 pixel_list=DestroyPixelListThreadSet(pixel_list);
3077 if (status == MagickFalse)
3078 statistic_image=DestroyImage(statistic_image);
3079 return(statistic_image);