statistic.c revision 50908ac2139298d3f39d279d75c5868caea64316
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
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        %
11%                                                                             %
12%                                                                             %
13%                     MagickCore Image Statistical Methods                    %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
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.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/animate.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/cache-private.h"
50#include "MagickCore/cache-view.h"
51#include "MagickCore/client.h"
52#include "MagickCore/color.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/composite.h"
57#include "MagickCore/composite-private.h"
58#include "MagickCore/compress.h"
59#include "MagickCore/constitute.h"
60#include "MagickCore/display.h"
61#include "MagickCore/draw.h"
62#include "MagickCore/enhance.h"
63#include "MagickCore/exception.h"
64#include "MagickCore/exception-private.h"
65#include "MagickCore/gem.h"
66#include "MagickCore/gem-private.h"
67#include "MagickCore/geometry.h"
68#include "MagickCore/list.h"
69#include "MagickCore/image-private.h"
70#include "MagickCore/magic.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/monitor.h"
75#include "MagickCore/monitor-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel-accessor.h"
79#include "MagickCore/profile.h"
80#include "MagickCore/quantize.h"
81#include "MagickCore/quantum-private.h"
82#include "MagickCore/random_.h"
83#include "MagickCore/random-private.h"
84#include "MagickCore/resource_.h"
85#include "MagickCore/segment.h"
86#include "MagickCore/semaphore.h"
87#include "MagickCore/signature-private.h"
88#include "MagickCore/statistic.h"
89#include "MagickCore/string_.h"
90#include "MagickCore/thread-private.h"
91#include "MagickCore/timer.h"
92#include "MagickCore/utility.h"
93#include "MagickCore/version.h"
94
95/*
96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97%                                                                             %
98%                                                                             %
99%                                                                             %
100%     E v a l u a t e I m a g e                                               %
101%                                                                             %
102%                                                                             %
103%                                                                             %
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105%
106%  EvaluateImage() applies a value to the image with an arithmetic, relational,
107%  or logical operator to an image. Use these operations to lighten or darken
108%  an image, to increase or decrease contrast in an image, or to produce the
109%  "negative" of an image.
110%
111%  The format of the EvaluateImage method is:
112%
113%      MagickBooleanType EvaluateImage(Image *image,
114%        const MagickEvaluateOperator op,const double value,
115%        ExceptionInfo *exception)
116%      MagickBooleanType EvaluateImages(Image *images,
117%        const MagickEvaluateOperator op,const double value,
118%        ExceptionInfo *exception)
119%
120%  A description of each parameter follows:
121%
122%    o image: the image.
123%
124%    o op: A channel op.
125%
126%    o value: A value value.
127%
128%    o exception: return any errors or warnings in this structure.
129%
130*/
131
132typedef struct _PixelChannels
133{
134  double
135    channel[CompositePixelChannel];
136} PixelChannels;
137
138static PixelChannels **DestroyPixelThreadSet(PixelChannels **pixels)
139{
140  register ssize_t
141    i;
142
143  assert(pixels != (PixelChannels **) NULL);
144  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
145    if (pixels[i] != (PixelChannels *) NULL)
146      pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]);
147  pixels=(PixelChannels **) RelinquishMagickMemory(pixels);
148  return(pixels);
149}
150
151static PixelChannels **AcquirePixelThreadSet(const Image *image,
152  const size_t number_images)
153{
154  register ssize_t
155    i;
156
157  PixelChannels
158    **pixels;
159
160  size_t
161    length,
162    number_threads;
163
164  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
165  pixels=(PixelChannels **) AcquireQuantumMemory(number_threads,
166    sizeof(*pixels));
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++)
171  {
172    register ssize_t
173      j;
174
175    length=image->columns;
176    if (length < number_images)
177      length=number_images;
178    pixels[i]=(PixelChannels *) AcquireQuantumMemory(length,sizeof(**pixels));
179    if (pixels[i] == (PixelChannels *) NULL)
180      return(DestroyPixelThreadSet(pixels));
181    for (j=0; j < (ssize_t) length; j++)
182    {
183      register ssize_t
184        k;
185
186      for (k=0; k < MaxPixelChannels; k++)
187        pixels[i][j].channel[k]=0.0;
188    }
189  }
190  return(pixels);
191}
192
193static inline double EvaluateMax(const double x,const double y)
194{
195  if (x > y)
196    return(x);
197  return(y);
198}
199
200#if defined(__cplusplus) || defined(c_plusplus)
201extern "C" {
202#endif
203
204static int IntensityCompare(const void *x,const void *y)
205{
206  const PixelChannels
207    *color_1,
208    *color_2;
209
210  double
211    distance;
212
213  register ssize_t
214    i;
215
216  color_1=(const PixelChannels *) x;
217  color_2=(const PixelChannels *) y;
218  distance=0.0;
219  for (i=0; i < MaxPixelChannels; i++)
220    distance+=color_1->channel[i]-(double) color_2->channel[i];
221  return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
222}
223
224#if defined(__cplusplus) || defined(c_plusplus)
225}
226#endif
227
228static inline double MagickMin(const double x,const double y)
229{
230  if (x < y)
231    return(x);
232  return(y);
233}
234
235static double ApplyEvaluateOperator(RandomInfo *random_info,const Quantum pixel,
236  const MagickEvaluateOperator op,const double value)
237{
238  double
239    result;
240
241  result=0.0;
242  switch (op)
243  {
244    case UndefinedEvaluateOperator:
245      break;
246    case AbsEvaluateOperator:
247    {
248      result=(double) fabs((double) (pixel+value));
249      break;
250    }
251    case AddEvaluateOperator:
252    {
253      result=(double) (pixel+value);
254      break;
255    }
256    case AddModulusEvaluateOperator:
257    {
258      /*
259        This returns a 'floored modulus' of the addition which is a positive
260        result.  It differs from % or fmod() that returns a 'truncated modulus'
261        result, where floor() is replaced by trunc() and could return a
262        negative result (which is clipped).
263      */
264      result=pixel+value;
265      result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
266      break;
267    }
268    case AndEvaluateOperator:
269    {
270      result=(double) ((size_t) pixel & (size_t) (value+0.5));
271      break;
272    }
273    case CosineEvaluateOperator:
274    {
275      result=(double) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
276        QuantumScale*pixel*value))+0.5));
277      break;
278    }
279    case DivideEvaluateOperator:
280    {
281      result=pixel/(value == 0.0 ? 1.0 : value);
282      break;
283    }
284    case ExponentialEvaluateOperator:
285    {
286      result=(double) (QuantumRange*exp((double) (value*QuantumScale*pixel)));
287      break;
288    }
289    case GaussianNoiseEvaluateOperator:
290    {
291      result=(double) GenerateDifferentialNoise(random_info,pixel,
292        GaussianNoise,value);
293      break;
294    }
295    case ImpulseNoiseEvaluateOperator:
296    {
297      result=(double) GenerateDifferentialNoise(random_info,pixel,ImpulseNoise,
298        value);
299      break;
300    }
301    case LaplacianNoiseEvaluateOperator:
302    {
303      result=(double) GenerateDifferentialNoise(random_info,pixel,
304        LaplacianNoise,value);
305      break;
306    }
307    case LeftShiftEvaluateOperator:
308    {
309      result=(double) ((size_t) pixel << (size_t) (value+0.5));
310      break;
311    }
312    case LogEvaluateOperator:
313    {
314      if ((QuantumScale*pixel) >= MagickEpsilon)
315        result=(double) (QuantumRange*log((double) (QuantumScale*value*pixel+
316          1.0))/log((double) (value+1.0)));
317      break;
318    }
319    case MaxEvaluateOperator:
320    {
321      result=(double) EvaluateMax((double) pixel,value);
322      break;
323    }
324    case MeanEvaluateOperator:
325    {
326      result=(double) (pixel+value);
327      break;
328    }
329    case MedianEvaluateOperator:
330    {
331      result=(double) (pixel+value);
332      break;
333    }
334    case MinEvaluateOperator:
335    {
336      result=(double) MagickMin((double) pixel,value);
337      break;
338    }
339    case MultiplicativeNoiseEvaluateOperator:
340    {
341      result=(double) GenerateDifferentialNoise(random_info,pixel,
342        MultiplicativeGaussianNoise,value);
343      break;
344    }
345    case MultiplyEvaluateOperator:
346    {
347      result=(double) (value*pixel);
348      break;
349    }
350    case OrEvaluateOperator:
351    {
352      result=(double) ((size_t) pixel | (size_t) (value+0.5));
353      break;
354    }
355    case PoissonNoiseEvaluateOperator:
356    {
357      result=(double) GenerateDifferentialNoise(random_info,pixel,PoissonNoise,
358        value);
359      break;
360    }
361    case PowEvaluateOperator:
362    {
363      result=(double) (QuantumRange*pow((double) (QuantumScale*pixel),(double)
364        value));
365      break;
366    }
367    case RightShiftEvaluateOperator:
368    {
369      result=(double) ((size_t) pixel >> (size_t) (value+0.5));
370      break;
371    }
372    case RootMeanSquareEvaluateOperator:
373    {
374      result=(double) (pixel*pixel+value);
375      break;
376    }
377    case SetEvaluateOperator:
378    {
379      result=value;
380      break;
381    }
382    case SineEvaluateOperator:
383    {
384      result=(double) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
385        QuantumScale*pixel*value))+0.5));
386      break;
387    }
388    case SubtractEvaluateOperator:
389    {
390      result=(double) (pixel-value);
391      break;
392    }
393    case SumEvaluateOperator:
394    {
395      result=(double) (pixel+value);
396      break;
397    }
398    case ThresholdEvaluateOperator:
399    {
400      result=(double) (((double) pixel <= value) ? 0 : QuantumRange);
401      break;
402    }
403    case ThresholdBlackEvaluateOperator:
404    {
405      result=(double) (((double) pixel <= value) ? 0 : pixel);
406      break;
407    }
408    case ThresholdWhiteEvaluateOperator:
409    {
410      result=(double) (((double) pixel > value) ? QuantumRange : pixel);
411      break;
412    }
413    case UniformNoiseEvaluateOperator:
414    {
415      result=(double) GenerateDifferentialNoise(random_info,pixel,UniformNoise,
416        value);
417      break;
418    }
419    case XorEvaluateOperator:
420    {
421      result=(double) ((size_t) pixel ^ (size_t) (value+0.5));
422      break;
423    }
424  }
425  return(result);
426}
427
428MagickExport Image *EvaluateImages(const Image *images,
429  const MagickEvaluateOperator op,ExceptionInfo *exception)
430{
431#define EvaluateImageTag  "Evaluate/Image"
432
433  CacheView
434    *evaluate_view;
435
436  Image
437    *image;
438
439  MagickBooleanType
440    status;
441
442  MagickOffsetType
443    progress;
444
445  PixelChannels
446    **restrict evaluate_pixels;
447
448  RandomInfo
449    **restrict random_info;
450
451  size_t
452    number_images;
453
454  ssize_t
455    y;
456
457#if defined(MAGICKCORE_OPENMP_SUPPORT)
458  unsigned long
459    key;
460#endif
461
462  assert(images != (Image *) NULL);
463  assert(images->signature == MagickSignature);
464  if (images->debug != MagickFalse)
465    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
466  assert(exception != (ExceptionInfo *) NULL);
467  assert(exception->signature == MagickSignature);
468  image=CloneImage(images,images->columns,images->rows,MagickTrue,
469    exception);
470  if (image == (Image *) NULL)
471    return((Image *) NULL);
472  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
473    {
474      image=DestroyImage(image);
475      return((Image *) NULL);
476    }
477  number_images=GetImageListLength(images);
478  evaluate_pixels=AcquirePixelThreadSet(images,number_images);
479  if (evaluate_pixels == (PixelChannels **) NULL)
480    {
481      image=DestroyImage(image);
482      (void) ThrowMagickException(exception,GetMagickModule(),
483        ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
484      return((Image *) NULL);
485    }
486  /*
487    Evaluate image pixels.
488  */
489  status=MagickTrue;
490  progress=0;
491  random_info=AcquireRandomInfoThreadSet();
492#if defined(MAGICKCORE_OPENMP_SUPPORT)
493  key=GetRandomSecretKey(random_info[0]);
494#endif
495  evaluate_view=AcquireAuthenticCacheView(image,exception);
496  if (op == MedianEvaluateOperator)
497    {
498#if defined(MAGICKCORE_OPENMP_SUPPORT)
499      #pragma omp parallel for schedule(static,4) shared(progress,status) \
500        magick_threads(image,images,image->rows,key == ~0UL)
501#endif
502      for (y=0; y < (ssize_t) image->rows; y++)
503      {
504        CacheView
505          *image_view;
506
507        const Image
508          *next;
509
510        const int
511          id = GetOpenMPThreadId();
512
513        register PixelChannels
514          *evaluate_pixel;
515
516        register Quantum
517          *restrict q;
518
519        register ssize_t
520          x;
521
522        if (status == MagickFalse)
523          continue;
524        q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
525          exception);
526        if (q == (Quantum *) NULL)
527          {
528            status=MagickFalse;
529            continue;
530          }
531        evaluate_pixel=evaluate_pixels[id];
532        for (x=0; x < (ssize_t) image->columns; x++)
533        {
534          register ssize_t
535            j,
536            k;
537
538          for (j=0; j < (ssize_t) number_images; j++)
539            for (k=0; k < MaxPixelChannels; k++)
540              evaluate_pixel[j].channel[k]=0.0;
541          next=images;
542          for (j=0; j < (ssize_t) number_images; j++)
543          {
544            register const Quantum
545              *p;
546
547            register ssize_t
548              i;
549
550            image_view=AcquireVirtualCacheView(next,exception);
551            p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
552            if (p == (const Quantum *) NULL)
553              {
554                image_view=DestroyCacheView(image_view);
555                break;
556              }
557            for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
558            {
559              PixelChannel channel=GetPixelChannelChannel(image,i);
560              PixelTrait evaluate_traits=GetPixelChannelTraits(image,channel);
561              PixelTrait traits=GetPixelChannelTraits(next,channel);
562              if ((traits == UndefinedPixelTrait) ||
563                  (evaluate_traits == UndefinedPixelTrait))
564                continue;
565              if ((evaluate_traits & UpdatePixelTrait) == 0)
566                continue;
567              evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(
568                random_info[id],GetPixelChannel(image,channel,p),op,
569                evaluate_pixel[j].channel[i]);
570            }
571            image_view=DestroyCacheView(image_view);
572            next=GetNextImageInList(next);
573          }
574          qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
575            IntensityCompare);
576          for (k=0; k < (ssize_t) GetPixelChannels(image); k++)
577            q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]);
578          q+=GetPixelChannels(image);
579        }
580        if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
581          status=MagickFalse;
582        if (images->progress_monitor != (MagickProgressMonitor) NULL)
583          {
584            MagickBooleanType
585              proceed;
586
587#if   defined(MAGICKCORE_OPENMP_SUPPORT)
588            #pragma omp critical (MagickCore_EvaluateImages)
589#endif
590            proceed=SetImageProgress(images,EvaluateImageTag,progress++,
591              image->rows);
592            if (proceed == MagickFalse)
593              status=MagickFalse;
594          }
595      }
596    }
597  else
598    {
599#if defined(MAGICKCORE_OPENMP_SUPPORT)
600      #pragma omp parallel for schedule(static,4) shared(progress,status) \
601        magick_threads(image,images,image->rows,key == ~0UL)
602#endif
603      for (y=0; y < (ssize_t) image->rows; y++)
604      {
605        CacheView
606          *image_view;
607
608        const Image
609          *next;
610
611        const int
612          id = GetOpenMPThreadId();
613
614        register ssize_t
615          i,
616          x;
617
618        register PixelChannels
619          *evaluate_pixel;
620
621        register Quantum
622          *restrict q;
623
624        ssize_t
625          j;
626
627        if (status == MagickFalse)
628          continue;
629        q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
630          exception);
631        if (q == (Quantum *) NULL)
632          {
633            status=MagickFalse;
634            continue;
635          }
636        evaluate_pixel=evaluate_pixels[id];
637        for (j=0; j < (ssize_t) image->columns; j++)
638          for (i=0; i < MaxPixelChannels; i++)
639            evaluate_pixel[j].channel[i]=0.0;
640        next=images;
641        for (j=0; j < (ssize_t) number_images; j++)
642        {
643          register const Quantum
644            *p;
645
646          image_view=AcquireVirtualCacheView(next,exception);
647          p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
648          if (p == (const Quantum *) NULL)
649            {
650              image_view=DestroyCacheView(image_view);
651              break;
652            }
653          for (x=0; x < (ssize_t) next->columns; x++)
654          {
655            register ssize_t
656              i;
657
658            if (GetPixelReadMask(next,p) == 0)
659              {
660                p+=GetPixelChannels(next);
661                continue;
662              }
663            for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
664            {
665              PixelChannel channel=GetPixelChannelChannel(image,i);
666              PixelTrait  traits=GetPixelChannelTraits(next,channel);
667              PixelTrait  evaluate_traits=GetPixelChannelTraits(image,channel);
668              if ((traits == UndefinedPixelTrait) ||
669                  (evaluate_traits == UndefinedPixelTrait))
670                continue;
671              if ((traits & UpdatePixelTrait) == 0)
672                continue;
673              evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
674                random_info[id],GetPixelChannel(image,channel,p),j == 0 ?
675                AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
676            }
677            p+=GetPixelChannels(next);
678          }
679          image_view=DestroyCacheView(image_view);
680          next=GetNextImageInList(next);
681        }
682        for (x=0; x < (ssize_t) image->columns; x++)
683        {
684          register ssize_t
685             i;
686
687          switch (op)
688          {
689            case MeanEvaluateOperator:
690            {
691              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
692                evaluate_pixel[x].channel[i]/=(double) number_images;
693              break;
694            }
695            case MultiplyEvaluateOperator:
696            {
697              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
698              {
699                register ssize_t
700                  j;
701
702                for (j=0; j < (ssize_t) (number_images-1); j++)
703                  evaluate_pixel[x].channel[i]*=QuantumScale;
704              }
705              break;
706            }
707            case RootMeanSquareEvaluateOperator:
708            {
709              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
710                evaluate_pixel[x].channel[i]=sqrt(evaluate_pixel[x].channel[i]/
711                  number_images);
712              break;
713            }
714            default:
715              break;
716          }
717        }
718        for (x=0; x < (ssize_t) image->columns; x++)
719        {
720          register ssize_t
721            i;
722
723          if (GetPixelReadMask(image,q) == 0)
724            {
725              q+=GetPixelChannels(image);
726              continue;
727            }
728          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
729          {
730            PixelChannel channel=GetPixelChannelChannel(image,i);
731            PixelTrait traits=GetPixelChannelTraits(image,channel);
732            if (traits == UndefinedPixelTrait)
733              continue;
734            if ((traits & UpdatePixelTrait) == 0)
735              continue;
736            q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
737          }
738          q+=GetPixelChannels(image);
739        }
740        if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
741          status=MagickFalse;
742        if (images->progress_monitor != (MagickProgressMonitor) NULL)
743          {
744            MagickBooleanType
745              proceed;
746
747#if   defined(MAGICKCORE_OPENMP_SUPPORT)
748            #pragma omp critical (MagickCore_EvaluateImages)
749#endif
750            proceed=SetImageProgress(images,EvaluateImageTag,progress++,
751              image->rows);
752            if (proceed == MagickFalse)
753              status=MagickFalse;
754          }
755      }
756    }
757  evaluate_view=DestroyCacheView(evaluate_view);
758  evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
759  random_info=DestroyRandomInfoThreadSet(random_info);
760  if (status == MagickFalse)
761    image=DestroyImage(image);
762  return(image);
763}
764
765MagickExport MagickBooleanType EvaluateImage(Image *image,
766  const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
767{
768  CacheView
769    *image_view;
770
771  MagickBooleanType
772    status;
773
774  MagickOffsetType
775    progress;
776
777  RandomInfo
778    **restrict random_info;
779
780  ssize_t
781    y;
782
783#if defined(MAGICKCORE_OPENMP_SUPPORT)
784  unsigned long
785    key;
786#endif
787
788  assert(image != (Image *) NULL);
789  assert(image->signature == MagickSignature);
790  if (image->debug != MagickFalse)
791    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
792  assert(exception != (ExceptionInfo *) NULL);
793  assert(exception->signature == MagickSignature);
794  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
795    return(MagickFalse);
796  status=MagickTrue;
797  progress=0;
798  random_info=AcquireRandomInfoThreadSet();
799#if defined(MAGICKCORE_OPENMP_SUPPORT)
800  key=GetRandomSecretKey(random_info[0]);
801#endif
802  image_view=AcquireAuthenticCacheView(image,exception);
803#if defined(MAGICKCORE_OPENMP_SUPPORT)
804  #pragma omp parallel for schedule(static,4) shared(progress,status) \
805    magick_threads(image,image,image->rows,key == ~0UL)
806#endif
807  for (y=0; y < (ssize_t) image->rows; y++)
808  {
809    const int
810      id = GetOpenMPThreadId();
811
812    register Quantum
813      *restrict q;
814
815    register ssize_t
816      x;
817
818    if (status == MagickFalse)
819      continue;
820    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
821    if (q == (Quantum *) NULL)
822      {
823        status=MagickFalse;
824        continue;
825      }
826    for (x=0; x < (ssize_t) image->columns; x++)
827    {
828      double
829        result;
830
831      register ssize_t
832        i;
833
834      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
835      {
836        PixelChannel channel=GetPixelChannelChannel(image,i);
837        PixelTrait traits=GetPixelChannelTraits(image,channel);
838        if (traits == UndefinedPixelTrait)
839          continue;
840        if (((traits & CopyPixelTrait) != 0) ||
841            (GetPixelReadMask(image,q) == 0))
842          continue;
843        result=ApplyEvaluateOperator(random_info[id],q[i],op,value);
844        if (op == MeanEvaluateOperator)
845          result/=2.0;
846        q[i]=ClampToQuantum(result);
847      }
848      q+=GetPixelChannels(image);
849    }
850    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
851      status=MagickFalse;
852    if (image->progress_monitor != (MagickProgressMonitor) NULL)
853      {
854        MagickBooleanType
855          proceed;
856
857#if defined(MAGICKCORE_OPENMP_SUPPORT)
858        #pragma omp critical (MagickCore_EvaluateImage)
859#endif
860        proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
861        if (proceed == MagickFalse)
862          status=MagickFalse;
863      }
864  }
865  image_view=DestroyCacheView(image_view);
866  random_info=DestroyRandomInfoThreadSet(random_info);
867  return(status);
868}
869
870/*
871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872%                                                                             %
873%                                                                             %
874%                                                                             %
875%     F u n c t i o n I m a g e                                               %
876%                                                                             %
877%                                                                             %
878%                                                                             %
879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880%
881%  FunctionImage() applies a value to the image with an arithmetic, relational,
882%  or logical operator to an image. Use these operations to lighten or darken
883%  an image, to increase or decrease contrast in an image, or to produce the
884%  "negative" of an image.
885%
886%  The format of the FunctionImage method is:
887%
888%      MagickBooleanType FunctionImage(Image *image,
889%        const MagickFunction function,const ssize_t number_parameters,
890%        const double *parameters,ExceptionInfo *exception)
891%
892%  A description of each parameter follows:
893%
894%    o image: the image.
895%
896%    o function: A channel function.
897%
898%    o parameters: one or more parameters.
899%
900%    o exception: return any errors or warnings in this structure.
901%
902*/
903
904static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
905  const size_t number_parameters,const double *parameters,
906  ExceptionInfo *exception)
907{
908  double
909    result;
910
911  register ssize_t
912    i;
913
914  (void) exception;
915  result=0.0;
916  switch (function)
917  {
918    case PolynomialFunction:
919    {
920      /*
921        Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+
922        c1*x^2+c2*x+c3).
923      */
924      result=0.0;
925      for (i=0; i < (ssize_t) number_parameters; i++)
926        result=result*QuantumScale*pixel+parameters[i];
927      result*=QuantumRange;
928      break;
929    }
930    case SinusoidFunction:
931    {
932      double
933        amplitude,
934        bias,
935        frequency,
936        phase;
937
938      /*
939        Sinusoid: frequency, phase, amplitude, bias.
940      */
941      frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
942      phase=(number_parameters >= 2) ? parameters[1] : 0.0;
943      amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
944      bias=(number_parameters >= 4) ? parameters[3] : 0.5;
945      result=(double) (QuantumRange*(amplitude*sin((double) (2.0*
946        MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
947      break;
948    }
949    case ArcsinFunction:
950    {
951      double
952        bias,
953        center,
954        range,
955        width;
956
957      /*
958        Arcsin (peged at range limits for invalid results): width, center,
959        range, and bias.
960      */
961      width=(number_parameters >= 1) ? parameters[0] : 1.0;
962      center=(number_parameters >= 2) ? parameters[1] : 0.5;
963      range=(number_parameters >= 3) ? parameters[2] : 1.0;
964      bias=(number_parameters >= 4) ? parameters[3] : 0.5;
965      result=2.0/width*(QuantumScale*pixel-center);
966      if ( result <= -1.0 )
967        result=bias-range/2.0;
968      else
969        if (result >= 1.0)
970          result=bias+range/2.0;
971        else
972          result=(double) (range/MagickPI*asin((double) result)+bias);
973      result*=QuantumRange;
974      break;
975    }
976    case ArctanFunction:
977    {
978      double
979        center,
980        bias,
981        range,
982        slope;
983
984      /*
985        Arctan: slope, center, range, and bias.
986      */
987      slope=(number_parameters >= 1) ? parameters[0] : 1.0;
988      center=(number_parameters >= 2) ? parameters[1] : 0.5;
989      range=(number_parameters >= 3) ? parameters[2] : 1.0;
990      bias=(number_parameters >= 4) ? parameters[3] : 0.5;
991      result=(double) (MagickPI*slope*(QuantumScale*pixel-center));
992      result=(double) (QuantumRange*(range/MagickPI*atan((double)
993        result)+bias));
994      break;
995    }
996    case UndefinedFunction:
997      break;
998  }
999  return(ClampToQuantum(result));
1000}
1001
1002MagickExport MagickBooleanType FunctionImage(Image *image,
1003  const MagickFunction function,const size_t number_parameters,
1004  const double *parameters,ExceptionInfo *exception)
1005{
1006#define FunctionImageTag  "Function/Image "
1007
1008  CacheView
1009    *image_view;
1010
1011  MagickBooleanType
1012    status;
1013
1014  MagickOffsetType
1015    progress;
1016
1017  ssize_t
1018    y;
1019
1020  assert(image != (Image *) NULL);
1021  assert(image->signature == MagickSignature);
1022  if (image->debug != MagickFalse)
1023    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1024  assert(exception != (ExceptionInfo *) NULL);
1025  assert(exception->signature == MagickSignature);
1026  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1027    return(MagickFalse);
1028  status=MagickTrue;
1029  progress=0;
1030  image_view=AcquireAuthenticCacheView(image,exception);
1031#if defined(MAGICKCORE_OPENMP_SUPPORT)
1032  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1033    magick_threads(image,image,image->rows,1)
1034#endif
1035  for (y=0; y < (ssize_t) image->rows; y++)
1036  {
1037    register Quantum
1038      *restrict q;
1039
1040    register ssize_t
1041      x;
1042
1043    if (status == MagickFalse)
1044      continue;
1045    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1046    if (q == (Quantum *) NULL)
1047      {
1048        status=MagickFalse;
1049        continue;
1050      }
1051    for (x=0; x < (ssize_t) image->columns; x++)
1052    {
1053      register ssize_t
1054        i;
1055
1056      if (GetPixelReadMask(image,q) == 0)
1057        {
1058          q+=GetPixelChannels(image);
1059          continue;
1060        }
1061      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1062      {
1063        PixelChannel channel=GetPixelChannelChannel(image,i);
1064        PixelTrait traits=GetPixelChannelTraits(image,channel);
1065        if (traits == UndefinedPixelTrait)
1066          continue;
1067        if ((traits & UpdatePixelTrait) == 0)
1068          continue;
1069        q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1070          exception);
1071      }
1072      q+=GetPixelChannels(image);
1073    }
1074    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1075      status=MagickFalse;
1076    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1077      {
1078        MagickBooleanType
1079          proceed;
1080
1081#if defined(MAGICKCORE_OPENMP_SUPPORT)
1082        #pragma omp critical (MagickCore_FunctionImage)
1083#endif
1084        proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1085        if (proceed == MagickFalse)
1086          status=MagickFalse;
1087      }
1088  }
1089  image_view=DestroyCacheView(image_view);
1090  return(status);
1091}
1092
1093/*
1094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1095%                                                                             %
1096%                                                                             %
1097%                                                                             %
1098%   G e t I m a g e E n t r o p y                                             %
1099%                                                                             %
1100%                                                                             %
1101%                                                                             %
1102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103%
1104%  GetImageEntropy() returns the entropy of one or more image channels.
1105%
1106%  The format of the GetImageEntropy method is:
1107%
1108%      MagickBooleanType GetImageEntropy(const Image *image,double *entropy,
1109%        ExceptionInfo *exception)
1110%
1111%  A description of each parameter follows:
1112%
1113%    o image: the image.
1114%
1115%    o entropy: the average entropy of the selected channels.
1116%
1117%    o exception: return any errors or warnings in this structure.
1118%
1119*/
1120MagickExport MagickBooleanType GetImageEntropy(const Image *image,
1121  double *entropy,ExceptionInfo *exception)
1122{
1123  double
1124    area;
1125
1126  ChannelStatistics
1127    *channel_statistics;
1128
1129  register ssize_t
1130    i;
1131
1132  assert(image != (Image *) NULL);
1133  assert(image->signature == MagickSignature);
1134  if (image->debug != MagickFalse)
1135    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1136  channel_statistics=GetImageStatistics(image,exception);
1137  if (channel_statistics == (ChannelStatistics *) NULL)
1138    return(MagickFalse);
1139  area=0.0;
1140  channel_statistics[CompositePixelChannel].entropy=0.0;
1141  channel_statistics[CompositePixelChannel].standard_deviation=0.0;
1142  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1143  {
1144    PixelChannel channel=GetPixelChannelChannel(image,i);
1145    PixelTrait traits=GetPixelChannelTraits(image,channel);
1146    if (traits == UndefinedPixelTrait)
1147      continue;
1148    if ((traits & UpdatePixelTrait) == 0)
1149      continue;
1150    channel_statistics[CompositePixelChannel].entropy+=
1151      channel_statistics[i].entropy;
1152    area++;
1153  }
1154  channel_statistics[CompositePixelChannel].entropy/=area;
1155  channel_statistics[CompositePixelChannel].standard_deviation=
1156    sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1157  *entropy=channel_statistics[CompositePixelChannel].entropy;
1158  channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1159    channel_statistics);
1160  return(MagickTrue);
1161}
1162
1163/*
1164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165%                                                                             %
1166%                                                                             %
1167%                                                                             %
1168%   G e t I m a g e E x t r e m a                                             %
1169%                                                                             %
1170%                                                                             %
1171%                                                                             %
1172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1173%
1174%  GetImageExtrema() returns the extrema of one or more image channels.
1175%
1176%  The format of the GetImageExtrema method is:
1177%
1178%      MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1179%        size_t *maxima,ExceptionInfo *exception)
1180%
1181%  A description of each parameter follows:
1182%
1183%    o image: the image.
1184%
1185%    o minima: the minimum value in the channel.
1186%
1187%    o maxima: the maximum value in the channel.
1188%
1189%    o exception: return any errors or warnings in this structure.
1190%
1191*/
1192MagickExport MagickBooleanType GetImageExtrema(const Image *image,
1193  size_t *minima,size_t *maxima,ExceptionInfo *exception)
1194{
1195  double
1196    max,
1197    min;
1198
1199  MagickBooleanType
1200    status;
1201
1202  assert(image != (Image *) NULL);
1203  assert(image->signature == MagickSignature);
1204  if (image->debug != MagickFalse)
1205    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1206  status=GetImageRange(image,&min,&max,exception);
1207  *minima=(size_t) ceil(min-0.5);
1208  *maxima=(size_t) floor(max+0.5);
1209  return(status);
1210}
1211
1212/*
1213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214%                                                                             %
1215%                                                                             %
1216%                                                                             %
1217%   G e t I m a g e K u r t o s i s                                           %
1218%                                                                             %
1219%                                                                             %
1220%                                                                             %
1221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222%
1223%  GetImageKurtosis() returns the kurtosis and skewness of one or more image
1224%  channels.
1225%
1226%  The format of the GetImageKurtosis method is:
1227%
1228%      MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1229%        double *skewness,ExceptionInfo *exception)
1230%
1231%  A description of each parameter follows:
1232%
1233%    o image: the image.
1234%
1235%    o kurtosis: the kurtosis of the channel.
1236%
1237%    o skewness: the skewness of the channel.
1238%
1239%    o exception: return any errors or warnings in this structure.
1240%
1241*/
1242MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1243  double *kurtosis,double *skewness,ExceptionInfo *exception)
1244{
1245  CacheView
1246    *image_view;
1247
1248  double
1249    area,
1250    mean,
1251    standard_deviation,
1252    sum_squares,
1253    sum_cubes,
1254    sum_fourth_power;
1255
1256  MagickBooleanType
1257    status;
1258
1259  ssize_t
1260    y;
1261
1262  assert(image != (Image *) NULL);
1263  assert(image->signature == MagickSignature);
1264  if (image->debug != MagickFalse)
1265    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1266  status=MagickTrue;
1267  *kurtosis=0.0;
1268  *skewness=0.0;
1269  area=0.0;
1270  mean=0.0;
1271  standard_deviation=0.0;
1272  sum_squares=0.0;
1273  sum_cubes=0.0;
1274  sum_fourth_power=0.0;
1275  image_view=AcquireVirtualCacheView(image,exception);
1276#if defined(MAGICKCORE_OPENMP_SUPPORT)
1277  #pragma omp parallel for schedule(static,4) shared(status) \
1278    magick_threads(image,image,image->rows,1)
1279#endif
1280  for (y=0; y < (ssize_t) image->rows; y++)
1281  {
1282    register const Quantum
1283      *restrict p;
1284
1285    register ssize_t
1286      x;
1287
1288    if (status == MagickFalse)
1289      continue;
1290    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1291    if (p == (const Quantum *) NULL)
1292      {
1293        status=MagickFalse;
1294        continue;
1295      }
1296    for (x=0; x < (ssize_t) image->columns; x++)
1297    {
1298      register ssize_t
1299        i;
1300
1301      if (GetPixelReadMask(image,p) == 0)
1302        {
1303          p+=GetPixelChannels(image);
1304          continue;
1305        }
1306      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1307      {
1308        PixelChannel channel=GetPixelChannelChannel(image,i);
1309        PixelTrait traits=GetPixelChannelTraits(image,channel);
1310        if (traits == UndefinedPixelTrait)
1311          continue;
1312        if ((traits & UpdatePixelTrait) == 0)
1313          continue;
1314#if defined(MAGICKCORE_OPENMP_SUPPORT)
1315        #pragma omp critical (MagickCore_GetImageKurtosis)
1316#endif
1317        {
1318          mean+=p[i];
1319          sum_squares+=(double) p[i]*p[i];
1320          sum_cubes+=(double) p[i]*p[i]*p[i];
1321          sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
1322          area++;
1323        }
1324      }
1325      p+=GetPixelChannels(image);
1326    }
1327  }
1328  image_view=DestroyCacheView(image_view);
1329  if (area != 0.0)
1330    {
1331      mean/=area;
1332      sum_squares/=area;
1333      sum_cubes/=area;
1334      sum_fourth_power/=area;
1335    }
1336  standard_deviation=sqrt(sum_squares-(mean*mean));
1337  if (standard_deviation != 0.0)
1338    {
1339      *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1340        3.0*mean*mean*mean*mean;
1341      *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1342        standard_deviation;
1343      *kurtosis-=3.0;
1344      *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1345      *skewness/=standard_deviation*standard_deviation*standard_deviation;
1346    }
1347  return(status);
1348}
1349
1350/*
1351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1352%                                                                             %
1353%                                                                             %
1354%                                                                             %
1355%   G e t I m a g e M e a n                                                   %
1356%                                                                             %
1357%                                                                             %
1358%                                                                             %
1359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1360%
1361%  GetImageMean() returns the mean and standard deviation of one or more image
1362%  channels.
1363%
1364%  The format of the GetImageMean method is:
1365%
1366%      MagickBooleanType GetImageMean(const Image *image,double *mean,
1367%        double *standard_deviation,ExceptionInfo *exception)
1368%
1369%  A description of each parameter follows:
1370%
1371%    o image: the image.
1372%
1373%    o mean: the average value in the channel.
1374%
1375%    o standard_deviation: the standard deviation of the channel.
1376%
1377%    o exception: return any errors or warnings in this structure.
1378%
1379*/
1380MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1381  double *standard_deviation,ExceptionInfo *exception)
1382{
1383  double
1384    area;
1385
1386  ChannelStatistics
1387    *channel_statistics;
1388
1389  register ssize_t
1390    i;
1391
1392  assert(image != (Image *) NULL);
1393  assert(image->signature == MagickSignature);
1394  if (image->debug != MagickFalse)
1395    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1396  channel_statistics=GetImageStatistics(image,exception);
1397  if (channel_statistics == (ChannelStatistics *) NULL)
1398    return(MagickFalse);
1399  area=0.0;
1400  channel_statistics[CompositePixelChannel].mean=0.0;
1401  channel_statistics[CompositePixelChannel].standard_deviation=0.0;
1402  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1403  {
1404    PixelChannel channel=GetPixelChannelChannel(image,i);
1405    PixelTrait traits=GetPixelChannelTraits(image,channel);
1406    if (traits == UndefinedPixelTrait)
1407      continue;
1408    if ((traits & UpdatePixelTrait) == 0)
1409      continue;
1410    channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
1411    channel_statistics[CompositePixelChannel].standard_deviation+=
1412      channel_statistics[i].variance-channel_statistics[i].mean*
1413      channel_statistics[i].mean;
1414    area++;
1415  }
1416  channel_statistics[CompositePixelChannel].mean/=area;
1417  channel_statistics[CompositePixelChannel].standard_deviation=
1418    sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1419  *mean=channel_statistics[CompositePixelChannel].mean;
1420  *standard_deviation=
1421    channel_statistics[CompositePixelChannel].standard_deviation;
1422  channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1423    channel_statistics);
1424  return(MagickTrue);
1425}
1426
1427/*
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429%                                                                             %
1430%                                                                             %
1431%                                                                             %
1432%   G e t I m a g e M o m e n t s                                             %
1433%                                                                             %
1434%                                                                             %
1435%                                                                             %
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437%
1438%  GetImageMoments() returns the normalized moments of one or more image
1439%  channels.
1440%
1441%  The format of the GetImageMoments method is:
1442%
1443%      ChannelMoments *GetImageMoments(const Image *image,
1444%        ExceptionInfo *exception)
1445%
1446%  A description of each parameter follows:
1447%
1448%    o image: the image.
1449%
1450%    o exception: return any errors or warnings in this structure.
1451%
1452*/
1453
1454static size_t GetImageChannels(const Image *image)
1455{
1456  register ssize_t
1457    i;
1458
1459  size_t
1460    channels;
1461
1462  channels=0;
1463  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1464  {
1465    PixelChannel channel=GetPixelChannelChannel(image,i);
1466    PixelTrait traits=GetPixelChannelTraits(image,channel);
1467    if ((traits & UpdatePixelTrait) != 0)
1468      channels++;
1469  }
1470  return((size_t) (channels == 0 ? 1 : channels));
1471}
1472
1473MagickExport ChannelMoments *GetImageMoments(const Image *image,
1474  ExceptionInfo *exception)
1475{
1476#define MaxNumberImageMoments  8
1477
1478  CacheView
1479    *image_view;
1480
1481  ChannelMoments
1482    *channel_moments;
1483
1484  double
1485    M00[MaxPixelChannels+1],
1486    M01[MaxPixelChannels+1],
1487    M02[MaxPixelChannels+1],
1488    M03[MaxPixelChannels+1],
1489    M10[MaxPixelChannels+1],
1490    M11[MaxPixelChannels+1],
1491    M12[MaxPixelChannels+1],
1492    M20[MaxPixelChannels+1],
1493    M21[MaxPixelChannels+1],
1494    M22[MaxPixelChannels+1],
1495    M30[MaxPixelChannels+1];
1496
1497  PointInfo
1498    centroid[MaxPixelChannels+1];
1499
1500  ssize_t
1501    channel,
1502    y;
1503
1504  assert(image != (Image *) NULL);
1505  assert(image->signature == MagickSignature);
1506  if (image->debug != MagickFalse)
1507    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1508  channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1509    sizeof(*channel_moments));
1510  if (channel_moments == (ChannelMoments *) NULL)
1511    return(channel_moments);
1512  (void) ResetMagickMemory(channel_moments,0,(MaxPixelChannels+1)*
1513    sizeof(*channel_moments));
1514  (void) ResetMagickMemory(centroid,0,sizeof(centroid));
1515  (void) ResetMagickMemory(M00,0,sizeof(M00));
1516  (void) ResetMagickMemory(M01,0,sizeof(M01));
1517  (void) ResetMagickMemory(M02,0,sizeof(M02));
1518  (void) ResetMagickMemory(M03,0,sizeof(M03));
1519  (void) ResetMagickMemory(M10,0,sizeof(M10));
1520  (void) ResetMagickMemory(M11,0,sizeof(M11));
1521  (void) ResetMagickMemory(M12,0,sizeof(M12));
1522  (void) ResetMagickMemory(M20,0,sizeof(M20));
1523  (void) ResetMagickMemory(M21,0,sizeof(M21));
1524  (void) ResetMagickMemory(M22,0,sizeof(M22));
1525  (void) ResetMagickMemory(M30,0,sizeof(M30));
1526  image_view=AcquireVirtualCacheView(image,exception);
1527  for (y=0; y < (ssize_t) image->rows; y++)
1528  {
1529    register const Quantum
1530      *restrict p;
1531
1532    register ssize_t
1533      x;
1534
1535    /*
1536      Compute center of mass (centroid).
1537    */
1538    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1539    if (p == (const Quantum *) NULL)
1540      break;
1541    for (x=0; x < (ssize_t) image->columns; x++)
1542    {
1543      register ssize_t
1544        i;
1545
1546      if (GetPixelReadMask(image,p) == 0)
1547        {
1548          p+=GetPixelChannels(image);
1549          continue;
1550        }
1551      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1552      {
1553        PixelChannel channel=GetPixelChannelChannel(image,i);
1554        PixelTrait traits=GetPixelChannelTraits(image,channel);
1555        if (traits == UndefinedPixelTrait)
1556          continue;
1557        if ((traits & UpdatePixelTrait) == 0)
1558          continue;
1559        M00[channel]+=QuantumScale*p[i];
1560        M00[MaxPixelChannels]+=QuantumScale*p[i];
1561        M10[channel]+=x*QuantumScale*p[i];
1562        M10[MaxPixelChannels]+=x*QuantumScale*p[i];
1563        M01[channel]+=y*QuantumScale*p[i];
1564        M01[MaxPixelChannels]+=y*QuantumScale*p[i];
1565      }
1566      p+=GetPixelChannels(image);
1567    }
1568  }
1569  for (channel=0; channel <= MaxPixelChannels; channel++)
1570  {
1571    /*
1572       Compute center of mass (centroid).
1573    */
1574    if (M00[channel] < MagickEpsilon)
1575      {
1576        M00[channel]+=MagickEpsilon;
1577        centroid[channel].x=(double) image->columns/2.0;
1578        centroid[channel].y=(double) image->rows/2.0;
1579        continue;
1580      }
1581    M00[channel]+=MagickEpsilon;
1582    centroid[channel].x=M10[channel]/M00[channel];
1583    centroid[channel].y=M01[channel]/M00[channel];
1584  }
1585  for (y=0; y < (ssize_t) image->rows; y++)
1586  {
1587    register const Quantum
1588      *restrict p;
1589
1590    register ssize_t
1591      x;
1592
1593    /*
1594      Compute the image moments.
1595    */
1596    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1597    if (p == (const Quantum *) NULL)
1598      break;
1599    for (x=0; x < (ssize_t) image->columns; x++)
1600    {
1601      register ssize_t
1602        i;
1603
1604      if (GetPixelReadMask(image,p) == 0)
1605        {
1606          p+=GetPixelChannels(image);
1607          continue;
1608        }
1609      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1610      {
1611        PixelChannel channel=GetPixelChannelChannel(image,i);
1612        PixelTrait traits=GetPixelChannelTraits(image,channel);
1613        if (traits == UndefinedPixelTrait)
1614          continue;
1615        if ((traits & UpdatePixelTrait) == 0)
1616          continue;
1617        M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1618          QuantumScale*p[i];
1619        M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1620          QuantumScale*p[i];
1621        M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1622          QuantumScale*p[i];
1623        M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1624          QuantumScale*p[i];
1625        M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1626          QuantumScale*p[i];
1627        M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1628          QuantumScale*p[i];
1629        M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1630          (y-centroid[channel].y)*QuantumScale*p[i];
1631        M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1632          (y-centroid[channel].y)*QuantumScale*p[i];
1633        M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1634          (y-centroid[channel].y)*QuantumScale*p[i];
1635        M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1636          (y-centroid[channel].y)*QuantumScale*p[i];
1637        M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1638          (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1639        M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1640          (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1641        M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1642          (x-centroid[channel].x)*QuantumScale*p[i];
1643        M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1644          (x-centroid[channel].x)*QuantumScale*p[i];
1645        M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1646          (y-centroid[channel].y)*QuantumScale*p[i];
1647        M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1648          (y-centroid[channel].y)*QuantumScale*p[i];
1649      }
1650      p+=GetPixelChannels(image);
1651    }
1652  }
1653  M00[MaxPixelChannels]/=GetImageChannels(image);
1654  M01[MaxPixelChannels]/=GetImageChannels(image);
1655  M02[MaxPixelChannels]/=GetImageChannels(image);
1656  M03[MaxPixelChannels]/=GetImageChannels(image);
1657  M10[MaxPixelChannels]/=GetImageChannels(image);
1658  M11[MaxPixelChannels]/=GetImageChannels(image);
1659  M12[MaxPixelChannels]/=GetImageChannels(image);
1660  M20[MaxPixelChannels]/=GetImageChannels(image);
1661  M21[MaxPixelChannels]/=GetImageChannels(image);
1662  M22[MaxPixelChannels]/=GetImageChannels(image);
1663  M30[MaxPixelChannels]/=GetImageChannels(image);
1664  for (channel=0; channel <= MaxPixelChannels; channel++)
1665  {
1666    /*
1667      Compute elliptical angle, major and minor axes, eccentricity, & intensity.
1668    */
1669    channel_moments[channel].centroid=centroid[channel];
1670    channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
1671      ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
1672      (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1673    channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
1674      ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
1675      (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1676    channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
1677      M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
1678    channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
1679      channel_moments[channel].ellipse_axis.y/
1680      (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
1681    channel_moments[channel].ellipse_intensity=M00[channel]/
1682      (MagickPI*channel_moments[channel].ellipse_axis.x*
1683      channel_moments[channel].ellipse_axis.y+MagickEpsilon);
1684  }
1685  for (channel=0; channel <= MaxPixelChannels; channel++)
1686  {
1687    /*
1688      Normalize image moments.
1689    */
1690    M10[channel]=0.0;
1691    M01[channel]=0.0;
1692    M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
1693    M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
1694    M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
1695    M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
1696    M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
1697    M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
1698    M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
1699    M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
1700    M00[channel]=1.0;
1701  }
1702  image_view=DestroyCacheView(image_view);
1703  for (channel=0; channel <= MaxPixelChannels; channel++)
1704  {
1705    /*
1706      Compute Hu invariant moments.
1707    */
1708    channel_moments[channel].invariant[0]=M20[channel]+M02[channel];
1709    channel_moments[channel].invariant[1]=(M20[channel]-M02[channel])*
1710      (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
1711    channel_moments[channel].invariant[2]=(M30[channel]-3.0*M12[channel])*
1712      (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
1713      (3.0*M21[channel]-M03[channel]);
1714    channel_moments[channel].invariant[3]=(M30[channel]+M12[channel])*
1715      (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
1716      (M21[channel]+M03[channel]);
1717    channel_moments[channel].invariant[4]=(M30[channel]-3.0*M12[channel])*
1718      (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1719      (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1720      (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
1721      (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1722      (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1723      (M21[channel]+M03[channel]));
1724    channel_moments[channel].invariant[5]=(M20[channel]-M02[channel])*
1725      ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
1726      (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
1727      4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
1728    channel_moments[channel].invariant[6]=(3.0*M21[channel]-M03[channel])*
1729      (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1730      (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1731      (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
1732      (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1733      (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1734      (M21[channel]+M03[channel]));
1735    channel_moments[channel].invariant[7]=M11[channel]*((M30[channel]+
1736      M12[channel])*(M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
1737      (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
1738      (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
1739  }
1740  if (y < (ssize_t) image->rows)
1741    channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1742  return(channel_moments);
1743}
1744
1745/*
1746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1747%                                                                             %
1748%                                                                             %
1749%                                                                             %
1750%   G e t I m a g e C h a n n e l P e r c e p t u a l H a s h                 %
1751%                                                                             %
1752%                                                                             %
1753%                                                                             %
1754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1755%
1756%  GetImagePerceptualHash() returns the perceptual hash of one or more
1757%  image channels.
1758%
1759%  The format of the GetImagePerceptualHash method is:
1760%
1761%      ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1762%        ExceptionInfo *exception)
1763%
1764%  A description of each parameter follows:
1765%
1766%    o image: the image.
1767%
1768%    o exception: return any errors or warnings in this structure.
1769%
1770*/
1771
1772static inline double MagickLog10(const double x)
1773{
1774#define Log10Epsilon  (1.0e-11)
1775
1776 if (fabs(x) < Log10Epsilon)
1777   return(log10(Log10Epsilon));
1778 return(log10(fabs(x)));
1779}
1780
1781MagickExport ChannelPerceptualHash *GetImagePerceptualHash(
1782  const Image *image,ExceptionInfo *exception)
1783{
1784  ChannelMoments
1785    *moments;
1786
1787  ChannelPerceptualHash
1788    *perceptual_hash;
1789
1790  Image
1791    *hash_image;
1792
1793  MagickBooleanType
1794    status;
1795
1796  register ssize_t
1797    i;
1798
1799  ssize_t
1800    channel;
1801
1802  /*
1803    Blur then transform to sRGB colorspace.
1804  */
1805  hash_image=BlurImage(image,0.0,1.0,exception);
1806  if (hash_image == (Image *) NULL)
1807    return((ChannelPerceptualHash *) NULL);
1808  hash_image->depth=8;
1809  status=TransformImageColorspace(hash_image,sRGBColorspace,exception);
1810  if (status == MagickFalse)
1811    return((ChannelPerceptualHash *) NULL);
1812  moments=GetImageMoments(hash_image,exception);
1813  hash_image=DestroyImage(hash_image);
1814  if (moments == (ChannelMoments *) NULL)
1815    return((ChannelPerceptualHash *) NULL);
1816  perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1817    CompositeChannels+1UL,sizeof(*perceptual_hash));
1818  if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1819    return((ChannelPerceptualHash *) NULL);
1820  for (channel=0; channel <= MaxPixelChannels; channel++)
1821    for (i=0; i < MaximumNumberOfImageMoments; i++)
1822      perceptual_hash[channel].srgb_hu_phash[i]=
1823        (-MagickLog10(moments[channel].invariant[i]));
1824  moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1825  /*
1826    Blur then transform to HCLp colorspace.
1827  */
1828  hash_image=BlurImage(image,0.0,1.0,exception);
1829  if (hash_image == (Image *) NULL)
1830    {
1831      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1832        perceptual_hash);
1833      return((ChannelPerceptualHash *) NULL);
1834    }
1835  hash_image->depth=8;
1836  status=TransformImageColorspace(hash_image,HCLpColorspace,exception);
1837  if (status == MagickFalse)
1838    {
1839      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1840        perceptual_hash);
1841      return((ChannelPerceptualHash *) NULL);
1842    }
1843  moments=GetImageMoments(hash_image,exception);
1844  hash_image=DestroyImage(hash_image);
1845  if (moments == (ChannelMoments *) NULL)
1846    {
1847      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1848        perceptual_hash);
1849      return((ChannelPerceptualHash *) NULL);
1850    }
1851  for (channel=0; channel <= MaxPixelChannels; channel++)
1852    for (i=0; i < MaximumNumberOfImageMoments; i++)
1853      perceptual_hash[channel].hclp_hu_phash[i]=
1854        (-MagickLog10(moments[channel].invariant[i]));
1855  moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1856  return(perceptual_hash);
1857}
1858
1859/*
1860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1861%                                                                             %
1862%                                                                             %
1863%                                                                             %
1864%   G e t I m a g e R a n g e                                                 %
1865%                                                                             %
1866%                                                                             %
1867%                                                                             %
1868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1869%
1870%  GetImageRange() returns the range of one or more image channels.
1871%
1872%  The format of the GetImageRange method is:
1873%
1874%      MagickBooleanType GetImageRange(const Image *image,double *minima,
1875%        double *maxima,ExceptionInfo *exception)
1876%
1877%  A description of each parameter follows:
1878%
1879%    o image: the image.
1880%
1881%    o minima: the minimum value in the channel.
1882%
1883%    o maxima: the maximum value in the channel.
1884%
1885%    o exception: return any errors or warnings in this structure.
1886%
1887*/
1888MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1889  double *maxima,ExceptionInfo *exception)
1890{
1891  CacheView
1892    *image_view;
1893
1894  MagickBooleanType
1895    initialize,
1896    status;
1897
1898  ssize_t
1899    y;
1900
1901  assert(image != (Image *) NULL);
1902  assert(image->signature == MagickSignature);
1903  if (image->debug != MagickFalse)
1904    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1905  status=MagickTrue;
1906  initialize=MagickTrue;
1907  *maxima=0.0;
1908  *minima=0.0;
1909  image_view=AcquireVirtualCacheView(image,exception);
1910#if defined(MAGICKCORE_OPENMP_SUPPORT)
1911  #pragma omp parallel for schedule(static,4) shared(status,initialize) \
1912    magick_threads(image,image,image->rows,1)
1913#endif
1914  for (y=0; y < (ssize_t) image->rows; y++)
1915  {
1916    register const Quantum
1917      *restrict p;
1918
1919    register ssize_t
1920      x;
1921
1922    if (status == MagickFalse)
1923      continue;
1924    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1925    if (p == (const Quantum *) NULL)
1926      {
1927        status=MagickFalse;
1928        continue;
1929      }
1930    for (x=0; x < (ssize_t) image->columns; x++)
1931    {
1932      register ssize_t
1933        i;
1934
1935      if (GetPixelReadMask(image,p) == 0)
1936        {
1937          p+=GetPixelChannels(image);
1938          continue;
1939        }
1940      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1941      {
1942        PixelChannel channel=GetPixelChannelChannel(image,i);
1943        PixelTrait traits=GetPixelChannelTraits(image,channel);
1944        if (traits == UndefinedPixelTrait)
1945          continue;
1946        if ((traits & UpdatePixelTrait) == 0)
1947          continue;
1948#if defined(MAGICKCORE_OPENMP_SUPPORT)
1949        #pragma omp critical (MagickCore_GetImageRange)
1950#endif
1951        {
1952          if (initialize != MagickFalse)
1953            {
1954              *minima=(double) p[i];
1955              *maxima=(double) p[i];
1956              initialize=MagickFalse;
1957            }
1958          else
1959            {
1960              if ((double) p[i] < *minima)
1961                *minima=(double) p[i];
1962              if ((double) p[i] > *maxima)
1963                *maxima=(double) p[i];
1964           }
1965        }
1966      }
1967      p+=GetPixelChannels(image);
1968    }
1969  }
1970  image_view=DestroyCacheView(image_view);
1971  return(status);
1972}
1973
1974/*
1975%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1976%                                                                             %
1977%                                                                             %
1978%                                                                             %
1979%   G e t I m a g e S t a t i s t i c s                                       %
1980%                                                                             %
1981%                                                                             %
1982%                                                                             %
1983%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1984%
1985%  GetImageStatistics() returns statistics for each channel in the image.  The
1986%  statistics include the channel depth, its minima, maxima, mean, standard
1987%  deviation, kurtosis and skewness.  You can access the red channel mean, for
1988%  example, like this:
1989%
1990%      channel_statistics=GetImageStatistics(image,exception);
1991%      red_mean=channel_statistics[RedPixelChannel].mean;
1992%
1993%  Use MagickRelinquishMemory() to free the statistics buffer.
1994%
1995%  The format of the GetImageStatistics method is:
1996%
1997%      ChannelStatistics *GetImageStatistics(const Image *image,
1998%        ExceptionInfo *exception)
1999%
2000%  A description of each parameter follows:
2001%
2002%    o image: the image.
2003%
2004%    o exception: return any errors or warnings in this structure.
2005%
2006*/
2007MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
2008  ExceptionInfo *exception)
2009{
2010  ChannelStatistics
2011    *channel_statistics;
2012
2013  double
2014    *histogram;
2015
2016  MagickStatusType
2017    status;
2018
2019  QuantumAny
2020    range;
2021
2022  register ssize_t
2023    i;
2024
2025  size_t
2026    channels,
2027    depth;
2028
2029  ssize_t
2030    y;
2031
2032  assert(image != (Image *) NULL);
2033  assert(image->signature == MagickSignature);
2034  if (image->debug != MagickFalse)
2035    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2036  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
2037    sizeof(*histogram));
2038  channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
2039    MaxPixelChannels+1,sizeof(*channel_statistics));
2040  if ((channel_statistics == (ChannelStatistics *) NULL) ||
2041      (histogram == (double *) NULL))
2042    {
2043      if (histogram != (double *) NULL)
2044        histogram=(double *) RelinquishMagickMemory(histogram);
2045      if (channel_statistics != (ChannelStatistics *) NULL)
2046        channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2047          channel_statistics);
2048      return(channel_statistics);
2049    }
2050  (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
2051    sizeof(*channel_statistics));
2052  for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2053  {
2054    channel_statistics[i].depth=1;
2055    channel_statistics[i].maxima=(-MagickMaximumValue);
2056    channel_statistics[i].minima=MagickMaximumValue;
2057  }
2058  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2059    sizeof(*histogram));
2060  for (y=0; y < (ssize_t) image->rows; y++)
2061  {
2062    register const Quantum
2063      *restrict p;
2064
2065    register ssize_t
2066      x;
2067
2068    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2069    if (p == (const Quantum *) NULL)
2070      break;
2071    for (x=0; x < (ssize_t) image->columns; x++)
2072    {
2073      register ssize_t
2074        i;
2075
2076      if (GetPixelReadMask(image,p) == 0)
2077        {
2078          p+=GetPixelChannels(image);
2079          continue;
2080        }
2081      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2082      {
2083        PixelChannel channel=GetPixelChannelChannel(image,i);
2084        PixelTrait traits=GetPixelChannelTraits(image,channel);
2085        if (traits == UndefinedPixelTrait)
2086          continue;
2087        if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
2088          {
2089            depth=channel_statistics[channel].depth;
2090            range=GetQuantumRange(depth);
2091            status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2092              range) ? MagickTrue : MagickFalse;
2093            if (status != MagickFalse)
2094              {
2095                channel_statistics[channel].depth++;
2096                i--;
2097                continue;
2098              }
2099          }
2100        if ((double) p[i] < channel_statistics[channel].minima)
2101          channel_statistics[channel].minima=(double) p[i];
2102        if ((double) p[i] > channel_statistics[channel].maxima)
2103          channel_statistics[channel].maxima=(double) p[i];
2104        channel_statistics[channel].sum+=p[i];
2105        channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
2106        channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
2107        channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
2108          p[i];
2109        channel_statistics[channel].area++;
2110        histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2111          ClampToQuantum((double) p[i]))+i]++;
2112      }
2113      p+=GetPixelChannels(image);
2114    }
2115  }
2116  for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2117  {
2118    double
2119      area;
2120
2121    register ssize_t
2122      j;
2123
2124    area=PerceptibleReciprocal(channel_statistics[i].area);
2125    channel_statistics[i].sum*=area;
2126    channel_statistics[i].sum_squared*=area;
2127    channel_statistics[i].sum_cubed*=area;
2128    channel_statistics[i].sum_fourth_power*=area;
2129    channel_statistics[i].mean=channel_statistics[i].sum;
2130    channel_statistics[i].variance=channel_statistics[i].sum_squared;
2131    channel_statistics[i].standard_deviation=sqrt(
2132      channel_statistics[i].variance-(channel_statistics[i].mean*
2133      channel_statistics[i].mean));
2134    for (j=0; j < (ssize_t) (MaxMap+1U); j++)
2135    {
2136      double
2137        count;
2138
2139      count=histogram[GetPixelChannels(image)*j+i]*area;
2140      channel_statistics[i].entropy+=-count*MagickLog10(count)/
2141        MagickLog10(MaxMap+1.0);
2142    }
2143  }
2144  for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2145  {
2146    channel_statistics[CompositePixelChannel].area+=channel_statistics[i].area;
2147    channel_statistics[CompositePixelChannel].minima=MagickMin(
2148      channel_statistics[CompositePixelChannel].minima,
2149      channel_statistics[i].minima);
2150    channel_statistics[CompositePixelChannel].maxima=EvaluateMax(
2151      channel_statistics[CompositePixelChannel].maxima,
2152      channel_statistics[i].maxima);
2153    channel_statistics[CompositePixelChannel].sum+=channel_statistics[i].sum;
2154    channel_statistics[CompositePixelChannel].sum_squared+=
2155      channel_statistics[i].sum_squared;
2156    channel_statistics[CompositePixelChannel].sum_cubed+=
2157      channel_statistics[i].sum_cubed;
2158    channel_statistics[CompositePixelChannel].sum_fourth_power+=
2159      channel_statistics[i].sum_fourth_power;
2160    channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
2161    channel_statistics[CompositePixelChannel].variance+=
2162      channel_statistics[i].variance-channel_statistics[i].mean*
2163      channel_statistics[i].mean;
2164    channel_statistics[CompositePixelChannel].standard_deviation+=
2165      channel_statistics[i].variance-channel_statistics[i].mean*
2166      channel_statistics[i].mean;
2167    if (channel_statistics[i].entropy > MagickEpsilon)
2168      channel_statistics[CompositePixelChannel].entropy+=
2169        channel_statistics[i].entropy;
2170  }
2171  channels=GetImageChannels(image);
2172  channel_statistics[CompositePixelChannel].area/=channels;
2173  channel_statistics[CompositePixelChannel].sum/=channels;
2174  channel_statistics[CompositePixelChannel].sum_squared/=channels;
2175  channel_statistics[CompositePixelChannel].sum_cubed/=channels;
2176  channel_statistics[CompositePixelChannel].sum_fourth_power/=channels;
2177  channel_statistics[CompositePixelChannel].mean/=channels;
2178  channel_statistics[CompositePixelChannel].variance/=channels;
2179  channel_statistics[CompositePixelChannel].standard_deviation=
2180    sqrt(channel_statistics[CompositePixelChannel].standard_deviation/channels);
2181  channel_statistics[CompositePixelChannel].kurtosis/=channels;
2182  channel_statistics[CompositePixelChannel].skewness/=channels;
2183  channel_statistics[CompositePixelChannel].entropy/=channels;
2184  for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2185  {
2186    double
2187      standard_deviation;
2188
2189    if (channel_statistics[i].standard_deviation == 0.0)
2190      continue;
2191    standard_deviation=PerceptibleReciprocal(
2192      channel_statistics[i].standard_deviation);
2193    channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
2194      channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
2195      channel_statistics[i].mean*channel_statistics[i].mean*
2196      channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2197      standard_deviation);
2198    channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
2199      channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
2200      channel_statistics[i].mean*channel_statistics[i].mean*
2201      channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
2202      channel_statistics[i].mean*1.0*channel_statistics[i].mean*
2203      channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2204      standard_deviation*standard_deviation)-3.0;
2205  }
2206  histogram=(double *) RelinquishMagickMemory(histogram);
2207  if (y < (ssize_t) image->rows)
2208    channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2209      channel_statistics);
2210  return(channel_statistics);
2211}
2212
2213/*
2214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2215%                                                                             %
2216%                                                                             %
2217%                                                                             %
2218%     P o l y n o m i a l I m a g e                                           %
2219%                                                                             %
2220%                                                                             %
2221%                                                                             %
2222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2223%
2224%  PolynomialImage() returns a new image where each pixel is the sum of the
2225%  pixels in the image sequence after applying its corresponding terms
2226%  (coefficient and degree pairs).
2227%
2228%  The format of the PolynomialImage method is:
2229%
2230%      Image *PolynomialImage(const Image *images,const size_t number_terms,
2231%        const double *terms,ExceptionInfo *exception)
2232%
2233%  A description of each parameter follows:
2234%
2235%    o images: the image sequence.
2236%
2237%    o number_terms: the number of terms in the list.  The actual list length
2238%      is 2 x number_terms + 1 (the constant).
2239%
2240%    o terms: the list of polynomial coefficients and degree pairs and a
2241%      constant.
2242%
2243%    o exception: return any errors or warnings in this structure.
2244%
2245*/
2246
2247MagickExport Image *PolynomialImage(const Image *images,
2248  const size_t number_terms,const double *terms,ExceptionInfo *exception)
2249{
2250#define PolynomialImageTag  "Polynomial/Image"
2251
2252  CacheView
2253    *polynomial_view;
2254
2255  Image
2256    *image;
2257
2258  MagickBooleanType
2259    status;
2260
2261  MagickOffsetType
2262    progress;
2263
2264  PixelChannels
2265    **restrict polynomial_pixels;
2266
2267  size_t
2268    number_images;
2269
2270  ssize_t
2271    y;
2272
2273  assert(images != (Image *) NULL);
2274  assert(images->signature == MagickSignature);
2275  if (images->debug != MagickFalse)
2276    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2277  assert(exception != (ExceptionInfo *) NULL);
2278  assert(exception->signature == MagickSignature);
2279  image=CloneImage(images,images->columns,images->rows,MagickTrue,
2280    exception);
2281  if (image == (Image *) NULL)
2282    return((Image *) NULL);
2283  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2284    {
2285      image=DestroyImage(image);
2286      return((Image *) NULL);
2287    }
2288  number_images=GetImageListLength(images);
2289  polynomial_pixels=AcquirePixelThreadSet(images,number_images);
2290  if (polynomial_pixels == (PixelChannels **) NULL)
2291    {
2292      image=DestroyImage(image);
2293      (void) ThrowMagickException(exception,GetMagickModule(),
2294        ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2295      return((Image *) NULL);
2296    }
2297  /*
2298    Polynomial image pixels.
2299  */
2300  status=MagickTrue;
2301  progress=0;
2302  polynomial_view=AcquireAuthenticCacheView(image,exception);
2303#if defined(MAGICKCORE_OPENMP_SUPPORT)
2304  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2305    magick_threads(image,image,image->rows,1)
2306#endif
2307  for (y=0; y < (ssize_t) image->rows; y++)
2308  {
2309    CacheView
2310      *image_view;
2311
2312    const Image
2313      *next;
2314
2315    const int
2316      id = GetOpenMPThreadId();
2317
2318    register ssize_t
2319      i,
2320      x;
2321
2322    register PixelChannels
2323      *polynomial_pixel;
2324
2325    register Quantum
2326      *restrict q;
2327
2328    ssize_t
2329      j;
2330
2331    if (status == MagickFalse)
2332      continue;
2333    q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2334      exception);
2335    if (q == (Quantum *) NULL)
2336      {
2337        status=MagickFalse;
2338        continue;
2339      }
2340    polynomial_pixel=polynomial_pixels[id];
2341    for (j=0; j < (ssize_t) image->columns; j++)
2342      for (i=0; i < MaxPixelChannels; i++)
2343        polynomial_pixel[j].channel[i]=0.0;
2344    next=images;
2345    for (j=0; j < (ssize_t) number_images; j++)
2346    {
2347      register const Quantum
2348        *p;
2349
2350      if (j >= (ssize_t) number_terms)
2351        continue;
2352      image_view=AcquireVirtualCacheView(next,exception);
2353      p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2354      if (p == (const Quantum *) NULL)
2355        {
2356          image_view=DestroyCacheView(image_view);
2357          break;
2358        }
2359      for (x=0; x < (ssize_t) image->columns; x++)
2360      {
2361        register ssize_t
2362          i;
2363
2364        if (GetPixelReadMask(next,p) == 0)
2365          {
2366            p+=GetPixelChannels(next);
2367            continue;
2368          }
2369        for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2370        {
2371          MagickRealType
2372            coefficient,
2373            degree;
2374
2375          PixelChannel channel=GetPixelChannelChannel(image,i);
2376          PixelTrait traits=GetPixelChannelTraits(next,channel);
2377          PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
2378          if ((traits == UndefinedPixelTrait) ||
2379              (polynomial_traits == UndefinedPixelTrait))
2380            continue;
2381          if ((traits & UpdatePixelTrait) == 0)
2382            continue;
2383          coefficient=(MagickRealType) terms[2*i];
2384          degree=(MagickRealType) terms[(i << 1)+1];
2385          polynomial_pixel[x].channel[i]+=coefficient*
2386            pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
2387        }
2388        p+=GetPixelChannels(next);
2389      }
2390      image_view=DestroyCacheView(image_view);
2391      next=GetNextImageInList(next);
2392    }
2393    for (x=0; x < (ssize_t) image->columns; x++)
2394    {
2395      register ssize_t
2396        i;
2397
2398      if (GetPixelReadMask(image,q) == 0)
2399        {
2400          q+=GetPixelChannels(image);
2401          continue;
2402        }
2403      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2404      {
2405        PixelChannel channel=GetPixelChannelChannel(image,i);
2406        PixelTrait traits=GetPixelChannelTraits(image,channel);
2407        if (traits == UndefinedPixelTrait)
2408          continue;
2409        if ((traits & UpdatePixelTrait) == 0)
2410          continue;
2411        q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
2412      }
2413      q+=GetPixelChannels(image);
2414    }
2415    if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2416      status=MagickFalse;
2417    if (images->progress_monitor != (MagickProgressMonitor) NULL)
2418      {
2419        MagickBooleanType
2420          proceed;
2421
2422#if defined(MAGICKCORE_OPENMP_SUPPORT)
2423        #pragma omp critical (MagickCore_PolynomialImages)
2424#endif
2425        proceed=SetImageProgress(images,PolynomialImageTag,progress++,
2426          image->rows);
2427        if (proceed == MagickFalse)
2428          status=MagickFalse;
2429      }
2430  }
2431  polynomial_view=DestroyCacheView(polynomial_view);
2432  polynomial_pixels=DestroyPixelThreadSet(polynomial_pixels);
2433  if (status == MagickFalse)
2434    image=DestroyImage(image);
2435  return(image);
2436}
2437
2438/*
2439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2440%                                                                             %
2441%                                                                             %
2442%                                                                             %
2443%     S t a t i s t i c I m a g e                                             %
2444%                                                                             %
2445%                                                                             %
2446%                                                                             %
2447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2448%
2449%  StatisticImage() makes each pixel the min / max / median / mode / etc. of
2450%  the neighborhood of the specified width and height.
2451%
2452%  The format of the StatisticImage method is:
2453%
2454%      Image *StatisticImage(const Image *image,const StatisticType type,
2455%        const size_t width,const size_t height,ExceptionInfo *exception)
2456%
2457%  A description of each parameter follows:
2458%
2459%    o image: the image.
2460%
2461%    o type: the statistic type (median, mode, etc.).
2462%
2463%    o width: the width of the pixel neighborhood.
2464%
2465%    o height: the height of the pixel neighborhood.
2466%
2467%    o exception: return any errors or warnings in this structure.
2468%
2469*/
2470
2471typedef struct _SkipNode
2472{
2473  size_t
2474    next[9],
2475    count,
2476    signature;
2477} SkipNode;
2478
2479typedef struct _SkipList
2480{
2481  ssize_t
2482    level;
2483
2484  SkipNode
2485    *nodes;
2486} SkipList;
2487
2488typedef struct _PixelList
2489{
2490  size_t
2491    length,
2492    seed;
2493
2494  SkipList
2495    skip_list;
2496
2497  size_t
2498    signature;
2499} PixelList;
2500
2501static PixelList *DestroyPixelList(PixelList *pixel_list)
2502{
2503  if (pixel_list == (PixelList *) NULL)
2504    return((PixelList *) NULL);
2505  if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2506    pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
2507      pixel_list->skip_list.nodes);
2508  pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2509  return(pixel_list);
2510}
2511
2512static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2513{
2514  register ssize_t
2515    i;
2516
2517  assert(pixel_list != (PixelList **) NULL);
2518  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2519    if (pixel_list[i] != (PixelList *) NULL)
2520      pixel_list[i]=DestroyPixelList(pixel_list[i]);
2521  pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2522  return(pixel_list);
2523}
2524
2525static PixelList *AcquirePixelList(const size_t width,const size_t height)
2526{
2527  PixelList
2528    *pixel_list;
2529
2530  pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2531  if (pixel_list == (PixelList *) NULL)
2532    return(pixel_list);
2533  (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2534  pixel_list->length=width*height;
2535  pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
2536    sizeof(*pixel_list->skip_list.nodes));
2537  if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2538    return(DestroyPixelList(pixel_list));
2539  (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
2540    sizeof(*pixel_list->skip_list.nodes));
2541  pixel_list->signature=MagickSignature;
2542  return(pixel_list);
2543}
2544
2545static PixelList **AcquirePixelListThreadSet(const size_t width,
2546  const size_t height)
2547{
2548  PixelList
2549    **pixel_list;
2550
2551  register ssize_t
2552    i;
2553
2554  size_t
2555    number_threads;
2556
2557  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2558  pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2559    sizeof(*pixel_list));
2560  if (pixel_list == (PixelList **) NULL)
2561    return((PixelList **) NULL);
2562  (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2563  for (i=0; i < (ssize_t) number_threads; i++)
2564  {
2565    pixel_list[i]=AcquirePixelList(width,height);
2566    if (pixel_list[i] == (PixelList *) NULL)
2567      return(DestroyPixelListThreadSet(pixel_list));
2568  }
2569  return(pixel_list);
2570}
2571
2572static void AddNodePixelList(PixelList *pixel_list,const size_t color)
2573{
2574  register SkipList
2575    *p;
2576
2577  register ssize_t
2578    level;
2579
2580  size_t
2581    search,
2582    update[9];
2583
2584  /*
2585    Initialize the node.
2586  */
2587  p=(&pixel_list->skip_list);
2588  p->nodes[color].signature=pixel_list->signature;
2589  p->nodes[color].count=1;
2590  /*
2591    Determine where it belongs in the list.
2592  */
2593  search=65536UL;
2594  for (level=p->level; level >= 0; level--)
2595  {
2596    while (p->nodes[search].next[level] < color)
2597      search=p->nodes[search].next[level];
2598    update[level]=search;
2599  }
2600  /*
2601    Generate a pseudo-random level for this node.
2602  */
2603  for (level=0; ; level++)
2604  {
2605    pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2606    if ((pixel_list->seed & 0x300) != 0x300)
2607      break;
2608  }
2609  if (level > 8)
2610    level=8;
2611  if (level > (p->level+2))
2612    level=p->level+2;
2613  /*
2614    If we're raising the list's level, link back to the root node.
2615  */
2616  while (level > p->level)
2617  {
2618    p->level++;
2619    update[p->level]=65536UL;
2620  }
2621  /*
2622    Link the node into the skip-list.
2623  */
2624  do
2625  {
2626    p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2627    p->nodes[update[level]].next[level]=color;
2628  } while (level-- > 0);
2629}
2630
2631static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
2632{
2633  register SkipList
2634    *p;
2635
2636  size_t
2637    color,
2638    maximum;
2639
2640  ssize_t
2641    count;
2642
2643  /*
2644    Find the maximum value for each of the color.
2645  */
2646  p=(&pixel_list->skip_list);
2647  color=65536L;
2648  count=0;
2649  maximum=p->nodes[color].next[0];
2650  do
2651  {
2652    color=p->nodes[color].next[0];
2653    if (color > maximum)
2654      maximum=color;
2655    count+=p->nodes[color].count;
2656  } while (count < (ssize_t) pixel_list->length);
2657  *pixel=ScaleShortToQuantum((unsigned short) maximum);
2658}
2659
2660static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
2661{
2662  double
2663    sum;
2664
2665  register SkipList
2666    *p;
2667
2668  size_t
2669    color;
2670
2671  ssize_t
2672    count;
2673
2674  /*
2675    Find the mean value for each of the color.
2676  */
2677  p=(&pixel_list->skip_list);
2678  color=65536L;
2679  count=0;
2680  sum=0.0;
2681  do
2682  {
2683    color=p->nodes[color].next[0];
2684    sum+=(double) p->nodes[color].count*color;
2685    count+=p->nodes[color].count;
2686  } while (count < (ssize_t) pixel_list->length);
2687  sum/=pixel_list->length;
2688  *pixel=ScaleShortToQuantum((unsigned short) sum);
2689}
2690
2691static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2692{
2693  register SkipList
2694    *p;
2695
2696  size_t
2697    color;
2698
2699  ssize_t
2700    count;
2701
2702  /*
2703    Find the median value for each of the color.
2704  */
2705  p=(&pixel_list->skip_list);
2706  color=65536L;
2707  count=0;
2708  do
2709  {
2710    color=p->nodes[color].next[0];
2711    count+=p->nodes[color].count;
2712  } while (count <= (ssize_t) (pixel_list->length >> 1));
2713  *pixel=ScaleShortToQuantum((unsigned short) color);
2714}
2715
2716static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
2717{
2718  register SkipList
2719    *p;
2720
2721  size_t
2722    color,
2723    minimum;
2724
2725  ssize_t
2726    count;
2727
2728  /*
2729    Find the minimum value for each of the color.
2730  */
2731  p=(&pixel_list->skip_list);
2732  count=0;
2733  color=65536UL;
2734  minimum=p->nodes[color].next[0];
2735  do
2736  {
2737    color=p->nodes[color].next[0];
2738    if (color < minimum)
2739      minimum=color;
2740    count+=p->nodes[color].count;
2741  } while (count < (ssize_t) pixel_list->length);
2742  *pixel=ScaleShortToQuantum((unsigned short) minimum);
2743}
2744
2745static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2746{
2747  register SkipList
2748    *p;
2749
2750  size_t
2751    color,
2752    max_count,
2753    mode;
2754
2755  ssize_t
2756    count;
2757
2758  /*
2759    Make each pixel the 'predominant color' of the specified neighborhood.
2760  */
2761  p=(&pixel_list->skip_list);
2762  color=65536L;
2763  mode=color;
2764  max_count=p->nodes[mode].count;
2765  count=0;
2766  do
2767  {
2768    color=p->nodes[color].next[0];
2769    if (p->nodes[color].count > max_count)
2770      {
2771        mode=color;
2772        max_count=p->nodes[mode].count;
2773      }
2774    count+=p->nodes[color].count;
2775  } while (count < (ssize_t) pixel_list->length);
2776  *pixel=ScaleShortToQuantum((unsigned short) mode);
2777}
2778
2779static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2780{
2781  register SkipList
2782    *p;
2783
2784  size_t
2785    color,
2786    next,
2787    previous;
2788
2789  ssize_t
2790    count;
2791
2792  /*
2793    Finds the non peak value for each of the colors.
2794  */
2795  p=(&pixel_list->skip_list);
2796  color=65536L;
2797  next=p->nodes[color].next[0];
2798  count=0;
2799  do
2800  {
2801    previous=color;
2802    color=next;
2803    next=p->nodes[color].next[0];
2804    count+=p->nodes[color].count;
2805  } while (count <= (ssize_t) (pixel_list->length >> 1));
2806  if ((previous == 65536UL) && (next != 65536UL))
2807    color=next;
2808  else
2809    if ((previous != 65536UL) && (next == 65536UL))
2810      color=previous;
2811  *pixel=ScaleShortToQuantum((unsigned short) color);
2812}
2813
2814static inline void GetRootMeanSquarePixelList(PixelList *pixel_list,
2815  Quantum *pixel)
2816{
2817  double
2818    sum;
2819
2820  register SkipList
2821    *p;
2822
2823  size_t
2824    color;
2825
2826  ssize_t
2827    count;
2828
2829  /*
2830    Find the root mean square value for each of the color.
2831  */
2832  p=(&pixel_list->skip_list);
2833  color=65536L;
2834  count=0;
2835  sum=0.0;
2836  do
2837  {
2838    color=p->nodes[color].next[0];
2839    sum+=(double) (p->nodes[color].count*color*color);
2840    count+=p->nodes[color].count;
2841  } while (count < (ssize_t) pixel_list->length);
2842  sum/=pixel_list->length;
2843  *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum));
2844}
2845
2846static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2847  Quantum *pixel)
2848{
2849  double
2850    sum,
2851    sum_squared;
2852
2853  register SkipList
2854    *p;
2855
2856  size_t
2857    color;
2858
2859  ssize_t
2860    count;
2861
2862  /*
2863    Find the standard-deviation value for each of the color.
2864  */
2865  p=(&pixel_list->skip_list);
2866  color=65536L;
2867  count=0;
2868  sum=0.0;
2869  sum_squared=0.0;
2870  do
2871  {
2872    register ssize_t
2873      i;
2874
2875    color=p->nodes[color].next[0];
2876    sum+=(double) p->nodes[color].count*color;
2877    for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2878      sum_squared+=((double) color)*((double) color);
2879    count+=p->nodes[color].count;
2880  } while (count < (ssize_t) pixel_list->length);
2881  sum/=pixel_list->length;
2882  sum_squared/=pixel_list->length;
2883  *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2884}
2885
2886static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
2887{
2888  size_t
2889    signature;
2890
2891  unsigned short
2892    index;
2893
2894  index=ScaleQuantumToShort(pixel);
2895  signature=pixel_list->skip_list.nodes[index].signature;
2896  if (signature == pixel_list->signature)
2897    {
2898      pixel_list->skip_list.nodes[index].count++;
2899      return;
2900    }
2901  AddNodePixelList(pixel_list,index);
2902}
2903
2904static inline double MagickAbsoluteValue(const double x)
2905{
2906  if (x < 0)
2907    return(-x);
2908  return(x);
2909}
2910
2911static inline size_t MagickMax(const size_t x,const size_t y)
2912{
2913  if (x > y)
2914    return(x);
2915  return(y);
2916}
2917
2918static void ResetPixelList(PixelList *pixel_list)
2919{
2920  int
2921    level;
2922
2923  register SkipNode
2924    *root;
2925
2926  register SkipList
2927    *p;
2928
2929  /*
2930    Reset the skip-list.
2931  */
2932  p=(&pixel_list->skip_list);
2933  root=p->nodes+65536UL;
2934  p->level=0;
2935  for (level=0; level < 9; level++)
2936    root->next[level]=65536UL;
2937  pixel_list->seed=pixel_list->signature++;
2938}
2939
2940MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2941  const size_t width,const size_t height,ExceptionInfo *exception)
2942{
2943#define StatisticImageTag  "Statistic/Image"
2944
2945  CacheView
2946    *image_view,
2947    *statistic_view;
2948
2949  Image
2950    *statistic_image;
2951
2952  MagickBooleanType
2953    status;
2954
2955  MagickOffsetType
2956    progress;
2957
2958  PixelList
2959    **restrict pixel_list;
2960
2961  ssize_t
2962    center,
2963    y;
2964
2965  /*
2966    Initialize statistics image attributes.
2967  */
2968  assert(image != (Image *) NULL);
2969  assert(image->signature == MagickSignature);
2970  if (image->debug != MagickFalse)
2971    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2972  assert(exception != (ExceptionInfo *) NULL);
2973  assert(exception->signature == MagickSignature);
2974  statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2975    exception);
2976  if (statistic_image == (Image *) NULL)
2977    return((Image *) NULL);
2978  status=SetImageStorageClass(statistic_image,DirectClass,exception);
2979  if (status == MagickFalse)
2980    {
2981      statistic_image=DestroyImage(statistic_image);
2982      return((Image *) NULL);
2983    }
2984  pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
2985  if (pixel_list == (PixelList **) NULL)
2986    {
2987      statistic_image=DestroyImage(statistic_image);
2988      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2989    }
2990  /*
2991    Make each pixel the min / max / median / mode / etc. of the neighborhood.
2992  */
2993  center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
2994    (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
2995  status=MagickTrue;
2996  progress=0;
2997  image_view=AcquireVirtualCacheView(image,exception);
2998  statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
2999#if defined(MAGICKCORE_OPENMP_SUPPORT)
3000  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3001    magick_threads(image,statistic_image,statistic_image->rows,1)
3002#endif
3003  for (y=0; y < (ssize_t) statistic_image->rows; y++)
3004  {
3005    const int
3006      id = GetOpenMPThreadId();
3007
3008    register const Quantum
3009      *restrict p;
3010
3011    register Quantum
3012      *restrict q;
3013
3014    register ssize_t
3015      x;
3016
3017    if (status == MagickFalse)
3018      continue;
3019    p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
3020      (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
3021      MagickMax(height,1),exception);
3022    q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
3023    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3024      {
3025        status=MagickFalse;
3026        continue;
3027      }
3028    for (x=0; x < (ssize_t) statistic_image->columns; x++)
3029    {
3030      register ssize_t
3031        i;
3032
3033      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3034      {
3035        Quantum
3036          pixel;
3037
3038        register const Quantum
3039          *restrict pixels;
3040
3041        register ssize_t
3042          u;
3043
3044        ssize_t
3045          v;
3046
3047        PixelChannel channel=GetPixelChannelChannel(image,i);
3048        PixelTrait traits=GetPixelChannelTraits(image,channel);
3049        PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
3050          channel);
3051        if ((traits == UndefinedPixelTrait) ||
3052            (statistic_traits == UndefinedPixelTrait))
3053          continue;
3054        if (((statistic_traits & CopyPixelTrait) != 0) ||
3055            (GetPixelReadMask(image,p) == 0))
3056          {
3057            SetPixelChannel(statistic_image,channel,p[center+i],q);
3058            continue;
3059          }
3060        pixels=p;
3061        ResetPixelList(pixel_list[id]);
3062        for (v=0; v < (ssize_t) MagickMax(height,1); v++)
3063        {
3064          for (u=0; u < (ssize_t) MagickMax(width,1); u++)
3065          {
3066            InsertPixelList(pixels[i],pixel_list[id]);
3067            pixels+=GetPixelChannels(image);
3068          }
3069          pixels+=(image->columns-1)*GetPixelChannels(image);
3070        }
3071        switch (type)
3072        {
3073          case GradientStatistic:
3074          {
3075            double
3076              maximum,
3077              minimum;
3078
3079            GetMinimumPixelList(pixel_list[id],&pixel);
3080            minimum=(double) pixel;
3081            GetMaximumPixelList(pixel_list[id],&pixel);
3082            maximum=(double) pixel;
3083            pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
3084            break;
3085          }
3086          case MaximumStatistic:
3087          {
3088            GetMaximumPixelList(pixel_list[id],&pixel);
3089            break;
3090          }
3091          case MeanStatistic:
3092          {
3093            GetMeanPixelList(pixel_list[id],&pixel);
3094            break;
3095          }
3096          case MedianStatistic:
3097          default:
3098          {
3099            GetMedianPixelList(pixel_list[id],&pixel);
3100            break;
3101          }
3102          case MinimumStatistic:
3103          {
3104            GetMinimumPixelList(pixel_list[id],&pixel);
3105            break;
3106          }
3107          case ModeStatistic:
3108          {
3109            GetModePixelList(pixel_list[id],&pixel);
3110            break;
3111          }
3112          case NonpeakStatistic:
3113          {
3114            GetNonpeakPixelList(pixel_list[id],&pixel);
3115            break;
3116          }
3117          case RootMeanSquareStatistic:
3118          {
3119            GetRootMeanSquarePixelList(pixel_list[id],&pixel);
3120            break;
3121          }
3122          case StandardDeviationStatistic:
3123          {
3124            GetStandardDeviationPixelList(pixel_list[id],&pixel);
3125            break;
3126          }
3127        }
3128        SetPixelChannel(statistic_image,channel,pixel,q);
3129      }
3130      p+=GetPixelChannels(image);
3131      q+=GetPixelChannels(statistic_image);
3132    }
3133    if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3134      status=MagickFalse;
3135    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3136      {
3137        MagickBooleanType
3138          proceed;
3139
3140#if defined(MAGICKCORE_OPENMP_SUPPORT)
3141        #pragma omp critical (MagickCore_StatisticImage)
3142#endif
3143        proceed=SetImageProgress(image,StatisticImageTag,progress++,
3144          image->rows);
3145        if (proceed == MagickFalse)
3146          status=MagickFalse;
3147      }
3148  }
3149  statistic_view=DestroyCacheView(statistic_view);
3150  image_view=DestroyCacheView(image_view);
3151  pixel_list=DestroyPixelListThreadSet(pixel_list);
3152  if (status == MagickFalse)
3153    statistic_image=DestroyImage(statistic_image);
3154  return(statistic_image);
3155}
3156