statistic.c revision 81c3360b2dfd109013aa2116eeb3a3caa29dd362
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 x t r e m a                                             %
1099%                                                                             %
1100%                                                                             %
1101%                                                                             %
1102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103%
1104%  GetImageExtrema() returns the extrema of one or more image channels.
1105%
1106%  The format of the GetImageExtrema method is:
1107%
1108%      MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1109%        size_t *maxima,ExceptionInfo *exception)
1110%
1111%  A description of each parameter follows:
1112%
1113%    o image: the image.
1114%
1115%    o minima: the minimum value in the channel.
1116%
1117%    o maxima: the maximum value in the channel.
1118%
1119%    o exception: return any errors or warnings in this structure.
1120%
1121*/
1122MagickExport MagickBooleanType GetImageExtrema(const Image *image,
1123  size_t *minima,size_t *maxima,ExceptionInfo *exception)
1124{
1125  double
1126    max,
1127    min;
1128
1129  MagickBooleanType
1130    status;
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  status=GetImageRange(image,&min,&max,exception);
1137  *minima=(size_t) ceil(min-0.5);
1138  *maxima=(size_t) floor(max+0.5);
1139  return(status);
1140}
1141
1142/*
1143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1144%                                                                             %
1145%                                                                             %
1146%                                                                             %
1147%   G e t I m a g e K u r t o s i s                                           %
1148%                                                                             %
1149%                                                                             %
1150%                                                                             %
1151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152%
1153%  GetImageKurtosis() returns the kurtosis and skewness of one or more image
1154%  channels.
1155%
1156%  The format of the GetImageKurtosis method is:
1157%
1158%      MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1159%        double *skewness,ExceptionInfo *exception)
1160%
1161%  A description of each parameter follows:
1162%
1163%    o image: the image.
1164%
1165%    o kurtosis: the kurtosis of the channel.
1166%
1167%    o skewness: the skewness of the channel.
1168%
1169%    o exception: return any errors or warnings in this structure.
1170%
1171*/
1172MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1173  double *kurtosis,double *skewness,ExceptionInfo *exception)
1174{
1175  CacheView
1176    *image_view;
1177
1178  double
1179    area,
1180    mean,
1181    standard_deviation,
1182    sum_squares,
1183    sum_cubes,
1184    sum_fourth_power;
1185
1186  MagickBooleanType
1187    status;
1188
1189  ssize_t
1190    y;
1191
1192  assert(image != (Image *) NULL);
1193  assert(image->signature == MagickSignature);
1194  if (image->debug != MagickFalse)
1195    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1196  status=MagickTrue;
1197  *kurtosis=0.0;
1198  *skewness=0.0;
1199  area=0.0;
1200  mean=0.0;
1201  standard_deviation=0.0;
1202  sum_squares=0.0;
1203  sum_cubes=0.0;
1204  sum_fourth_power=0.0;
1205  image_view=AcquireVirtualCacheView(image,exception);
1206#if defined(MAGICKCORE_OPENMP_SUPPORT)
1207  #pragma omp parallel for schedule(static,4) shared(status) \
1208    magick_threads(image,image,image->rows,1)
1209#endif
1210  for (y=0; y < (ssize_t) image->rows; y++)
1211  {
1212    register const Quantum
1213      *restrict p;
1214
1215    register ssize_t
1216      x;
1217
1218    if (status == MagickFalse)
1219      continue;
1220    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1221    if (p == (const Quantum *) NULL)
1222      {
1223        status=MagickFalse;
1224        continue;
1225      }
1226    for (x=0; x < (ssize_t) image->columns; x++)
1227    {
1228      register ssize_t
1229        i;
1230
1231      if (GetPixelReadMask(image,p) == 0)
1232        {
1233          p+=GetPixelChannels(image);
1234          continue;
1235        }
1236      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1237      {
1238        PixelChannel channel=GetPixelChannelChannel(image,i);
1239        PixelTrait traits=GetPixelChannelTraits(image,channel);
1240        if (traits == UndefinedPixelTrait)
1241          continue;
1242        if ((traits & UpdatePixelTrait) == 0)
1243          continue;
1244#if defined(MAGICKCORE_OPENMP_SUPPORT)
1245        #pragma omp critical (MagickCore_GetImageKurtosis)
1246#endif
1247        {
1248          mean+=p[i];
1249          sum_squares+=(double) p[i]*p[i];
1250          sum_cubes+=(double) p[i]*p[i]*p[i];
1251          sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
1252          area++;
1253        }
1254      }
1255      p+=GetPixelChannels(image);
1256    }
1257  }
1258  image_view=DestroyCacheView(image_view);
1259  if (area != 0.0)
1260    {
1261      mean/=area;
1262      sum_squares/=area;
1263      sum_cubes/=area;
1264      sum_fourth_power/=area;
1265    }
1266  standard_deviation=sqrt(sum_squares-(mean*mean));
1267  if (standard_deviation != 0.0)
1268    {
1269      *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1270        3.0*mean*mean*mean*mean;
1271      *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1272        standard_deviation;
1273      *kurtosis-=3.0;
1274      *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1275      *skewness/=standard_deviation*standard_deviation*standard_deviation;
1276    }
1277  return(status);
1278}
1279
1280/*
1281%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1282%                                                                             %
1283%                                                                             %
1284%                                                                             %
1285%   G e t I m a g e M e a n                                                   %
1286%                                                                             %
1287%                                                                             %
1288%                                                                             %
1289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290%
1291%  GetImageMean() returns the mean and standard deviation of one or more image
1292%  channels.
1293%
1294%  The format of the GetImageMean method is:
1295%
1296%      MagickBooleanType GetImageMean(const Image *image,double *mean,
1297%        double *standard_deviation,ExceptionInfo *exception)
1298%
1299%  A description of each parameter follows:
1300%
1301%    o image: the image.
1302%
1303%    o mean: the average value in the channel.
1304%
1305%    o standard_deviation: the standard deviation of the channel.
1306%
1307%    o exception: return any errors or warnings in this structure.
1308%
1309*/
1310MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1311  double *standard_deviation,ExceptionInfo *exception)
1312{
1313  double
1314    area;
1315
1316  ChannelStatistics
1317    *channel_statistics;
1318
1319  register ssize_t
1320    i;
1321
1322  assert(image != (Image *) NULL);
1323  assert(image->signature == MagickSignature);
1324  if (image->debug != MagickFalse)
1325    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1326  channel_statistics=GetImageStatistics(image,exception);
1327  if (channel_statistics == (ChannelStatistics *) NULL)
1328    return(MagickFalse);
1329  area=0.0;
1330  channel_statistics[CompositePixelChannel].mean=0.0;
1331  channel_statistics[CompositePixelChannel].standard_deviation=0.0;
1332  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1333  {
1334    PixelChannel channel=GetPixelChannelChannel(image,i);
1335    PixelTrait traits=GetPixelChannelTraits(image,channel);
1336    if (traits == UndefinedPixelTrait)
1337      continue;
1338    if ((traits & UpdatePixelTrait) == 0)
1339      continue;
1340    channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
1341    channel_statistics[CompositePixelChannel].standard_deviation+=
1342      channel_statistics[i].variance-channel_statistics[i].mean*
1343      channel_statistics[i].mean;
1344    area++;
1345  }
1346  channel_statistics[CompositePixelChannel].mean/=area;
1347  channel_statistics[CompositePixelChannel].standard_deviation=
1348    sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1349  *mean=channel_statistics[CompositePixelChannel].mean;
1350  *standard_deviation=
1351    channel_statistics[CompositePixelChannel].standard_deviation;
1352  channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1353    channel_statistics);
1354  return(MagickTrue);
1355}
1356
1357/*
1358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1359%                                                                             %
1360%                                                                             %
1361%                                                                             %
1362%   G e t I m a g e M o m e n t s                                             %
1363%                                                                             %
1364%                                                                             %
1365%                                                                             %
1366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1367%
1368%  GetImageMoments() returns the normalized moments of one or more image
1369%  channels.
1370%
1371%  The format of the GetImageMoments method is:
1372%
1373%      ChannelMoments *GetImageMoments(const Image *image,
1374%        ExceptionInfo *exception)
1375%
1376%  A description of each parameter follows:
1377%
1378%    o image: the image.
1379%
1380%    o exception: return any errors or warnings in this structure.
1381%
1382*/
1383
1384static size_t GetImageChannels(const Image *image)
1385{
1386  register ssize_t
1387    i;
1388
1389  size_t
1390    channels;
1391
1392  channels=0;
1393  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1394  {
1395    PixelChannel channel=GetPixelChannelChannel(image,i);
1396    PixelTrait traits=GetPixelChannelTraits(image,channel);
1397    if ((traits & UpdatePixelTrait) != 0)
1398      channels++;
1399  }
1400  return((size_t) (channels == 0 ? 1 : channels));
1401}
1402
1403MagickExport ChannelMoments *GetImageMoments(const Image *image,
1404  ExceptionInfo *exception)
1405{
1406#define MaxNumberImageMoments  8
1407
1408  CacheView
1409    *image_view;
1410
1411  ChannelMoments
1412    *channel_moments;
1413
1414  double
1415    M00[MaxPixelChannels+1],
1416    M01[MaxPixelChannels+1],
1417    M02[MaxPixelChannels+1],
1418    M03[MaxPixelChannels+1],
1419    M10[MaxPixelChannels+1],
1420    M11[MaxPixelChannels+1],
1421    M12[MaxPixelChannels+1],
1422    M20[MaxPixelChannels+1],
1423    M21[MaxPixelChannels+1],
1424    M22[MaxPixelChannels+1],
1425    M30[MaxPixelChannels+1];
1426
1427  PointInfo
1428    centroid[MaxPixelChannels+1];
1429
1430  ssize_t
1431    channel,
1432    y;
1433
1434  assert(image != (Image *) NULL);
1435  assert(image->signature == MagickSignature);
1436  if (image->debug != MagickFalse)
1437    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1438  channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
1439    sizeof(*channel_moments));
1440  if (channel_moments == (ChannelMoments *) NULL)
1441    return(channel_moments);
1442  (void) ResetMagickMemory(channel_moments,0,(MaxPixelChannels+1)*
1443    sizeof(*channel_moments));
1444  (void) ResetMagickMemory(centroid,0,sizeof(centroid));
1445  (void) ResetMagickMemory(M00,0,sizeof(M00));
1446  (void) ResetMagickMemory(M01,0,sizeof(M01));
1447  (void) ResetMagickMemory(M02,0,sizeof(M02));
1448  (void) ResetMagickMemory(M03,0,sizeof(M03));
1449  (void) ResetMagickMemory(M10,0,sizeof(M10));
1450  (void) ResetMagickMemory(M11,0,sizeof(M11));
1451  (void) ResetMagickMemory(M12,0,sizeof(M12));
1452  (void) ResetMagickMemory(M20,0,sizeof(M20));
1453  (void) ResetMagickMemory(M21,0,sizeof(M21));
1454  (void) ResetMagickMemory(M22,0,sizeof(M22));
1455  (void) ResetMagickMemory(M30,0,sizeof(M30));
1456  image_view=AcquireVirtualCacheView(image,exception);
1457  for (y=0; y < (ssize_t) image->rows; y++)
1458  {
1459    register const Quantum
1460      *restrict p;
1461
1462    register ssize_t
1463      x;
1464
1465    /*
1466      Compute center of mass (centroid).
1467    */
1468    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1469    if (p == (const Quantum *) NULL)
1470      break;
1471    for (x=0; x < (ssize_t) image->columns; x++)
1472    {
1473      register ssize_t
1474        i;
1475
1476      if (GetPixelReadMask(image,p) == 0)
1477        {
1478          p+=GetPixelChannels(image);
1479          continue;
1480        }
1481      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1482      {
1483        PixelChannel channel=GetPixelChannelChannel(image,i);
1484        PixelTrait traits=GetPixelChannelTraits(image,channel);
1485        if (traits == UndefinedPixelTrait)
1486          continue;
1487        if ((traits & UpdatePixelTrait) == 0)
1488          continue;
1489        M00[channel]+=QuantumScale*p[i];
1490        M00[MaxPixelChannels]+=QuantumScale*p[i];
1491        M10[channel]+=x*QuantumScale*p[i];
1492        M10[MaxPixelChannels]+=x*QuantumScale*p[i];
1493        M01[channel]+=y*QuantumScale*p[i];
1494        M01[MaxPixelChannels]+=y*QuantumScale*p[i];
1495      }
1496      p+=GetPixelChannels(image);
1497    }
1498  }
1499  for (channel=0; channel <= MaxPixelChannels; channel++)
1500  {
1501    /*
1502       Compute center of mass (centroid).
1503    */
1504    if (M00[channel] < MagickEpsilon)
1505      {
1506        M00[channel]+=MagickEpsilon;
1507        centroid[channel].x=(double) image->columns/2.0;
1508        centroid[channel].y=(double) image->rows/2.0;
1509        continue;
1510      }
1511    M00[channel]+=MagickEpsilon;
1512    centroid[channel].x=M10[channel]/M00[channel];
1513    centroid[channel].y=M01[channel]/M00[channel];
1514  }
1515  for (y=0; y < (ssize_t) image->rows; y++)
1516  {
1517    register const Quantum
1518      *restrict p;
1519
1520    register ssize_t
1521      x;
1522
1523    /*
1524      Compute the image moments.
1525    */
1526    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1527    if (p == (const Quantum *) NULL)
1528      break;
1529    for (x=0; x < (ssize_t) image->columns; x++)
1530    {
1531      register ssize_t
1532        i;
1533
1534      if (GetPixelReadMask(image,p) == 0)
1535        {
1536          p+=GetPixelChannels(image);
1537          continue;
1538        }
1539      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1540      {
1541        PixelChannel channel=GetPixelChannelChannel(image,i);
1542        PixelTrait traits=GetPixelChannelTraits(image,channel);
1543        if (traits == UndefinedPixelTrait)
1544          continue;
1545        if ((traits & UpdatePixelTrait) == 0)
1546          continue;
1547        M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1548          QuantumScale*p[i];
1549        M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1550          QuantumScale*p[i];
1551        M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1552          QuantumScale*p[i];
1553        M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1554          QuantumScale*p[i];
1555        M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1556          QuantumScale*p[i];
1557        M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1558          QuantumScale*p[i];
1559        M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1560          (y-centroid[channel].y)*QuantumScale*p[i];
1561        M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1562          (y-centroid[channel].y)*QuantumScale*p[i];
1563        M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1564          (y-centroid[channel].y)*QuantumScale*p[i];
1565        M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
1566          (y-centroid[channel].y)*QuantumScale*p[i];
1567        M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1568          (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1569        M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1570          (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
1571        M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1572          (x-centroid[channel].x)*QuantumScale*p[i];
1573        M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
1574          (x-centroid[channel].x)*QuantumScale*p[i];
1575        M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1576          (y-centroid[channel].y)*QuantumScale*p[i];
1577        M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
1578          (y-centroid[channel].y)*QuantumScale*p[i];
1579      }
1580      p+=GetPixelChannels(image);
1581    }
1582  }
1583  M00[MaxPixelChannels]/=GetImageChannels(image);
1584  M01[MaxPixelChannels]/=GetImageChannels(image);
1585  M02[MaxPixelChannels]/=GetImageChannels(image);
1586  M03[MaxPixelChannels]/=GetImageChannels(image);
1587  M10[MaxPixelChannels]/=GetImageChannels(image);
1588  M11[MaxPixelChannels]/=GetImageChannels(image);
1589  M12[MaxPixelChannels]/=GetImageChannels(image);
1590  M20[MaxPixelChannels]/=GetImageChannels(image);
1591  M21[MaxPixelChannels]/=GetImageChannels(image);
1592  M22[MaxPixelChannels]/=GetImageChannels(image);
1593  M30[MaxPixelChannels]/=GetImageChannels(image);
1594  for (channel=0; channel <= MaxPixelChannels; channel++)
1595  {
1596    /*
1597      Compute elliptical angle, major and minor axes, eccentricity, & intensity.
1598    */
1599    channel_moments[channel].centroid=centroid[channel];
1600    channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
1601      ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
1602      (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1603    channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
1604      ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
1605      (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
1606    channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
1607      M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
1608    channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
1609      channel_moments[channel].ellipse_axis.y/
1610      (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
1611    channel_moments[channel].ellipse_intensity=M00[channel]/
1612      (MagickPI*channel_moments[channel].ellipse_axis.x*
1613      channel_moments[channel].ellipse_axis.y+MagickEpsilon);
1614  }
1615  for (channel=0; channel <= MaxPixelChannels; channel++)
1616  {
1617    /*
1618      Normalize image moments.
1619    */
1620    M10[channel]=0.0;
1621    M01[channel]=0.0;
1622    M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
1623    M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
1624    M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
1625    M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
1626    M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
1627    M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
1628    M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
1629    M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
1630    M00[channel]=1.0;
1631  }
1632  image_view=DestroyCacheView(image_view);
1633  for (channel=0; channel <= MaxPixelChannels; channel++)
1634  {
1635    /*
1636      Compute Hu invariant moments.
1637    */
1638    channel_moments[channel].invariant[0]=M20[channel]+M02[channel];
1639    channel_moments[channel].invariant[1]=(M20[channel]-M02[channel])*
1640      (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
1641    channel_moments[channel].invariant[2]=(M30[channel]-3.0*M12[channel])*
1642      (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
1643      (3.0*M21[channel]-M03[channel]);
1644    channel_moments[channel].invariant[3]=(M30[channel]+M12[channel])*
1645      (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
1646      (M21[channel]+M03[channel]);
1647    channel_moments[channel].invariant[4]=(M30[channel]-3.0*M12[channel])*
1648      (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1649      (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1650      (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
1651      (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1652      (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1653      (M21[channel]+M03[channel]));
1654    channel_moments[channel].invariant[5]=(M20[channel]-M02[channel])*
1655      ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
1656      (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
1657      4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
1658    channel_moments[channel].invariant[6]=(3.0*M21[channel]-M03[channel])*
1659      (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
1660      (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
1661      (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
1662      (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
1663      (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
1664      (M21[channel]+M03[channel]));
1665    channel_moments[channel].invariant[7]=M11[channel]*((M30[channel]+
1666      M12[channel])*(M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
1667      (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
1668      (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
1669  }
1670  if (y < (ssize_t) image->rows)
1671    channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
1672  return(channel_moments);
1673}
1674
1675/*
1676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677%                                                                             %
1678%                                                                             %
1679%                                                                             %
1680%   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                 %
1681%                                                                             %
1682%                                                                             %
1683%                                                                             %
1684%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1685%
1686%  GetImagePerceptualHash() returns the perceptual hash of one or more
1687%  image channels.
1688%
1689%  The format of the GetImagePerceptualHash method is:
1690%
1691%      ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
1692%        ExceptionInfo *exception)
1693%
1694%  A description of each parameter follows:
1695%
1696%    o image: the image.
1697%
1698%    o exception: return any errors or warnings in this structure.
1699%
1700*/
1701
1702static inline double MagickLog10(const double x)
1703{
1704#define Log10Epsilon  (1.0e-11)
1705
1706 if (fabs(x) < Log10Epsilon)
1707   return(log10(Log10Epsilon));
1708 return(log10(fabs(x)));
1709}
1710
1711MagickExport ChannelPerceptualHash *GetImagePerceptualHash(
1712  const Image *image,ExceptionInfo *exception)
1713{
1714  ChannelMoments
1715    *moments;
1716
1717  ChannelPerceptualHash
1718    *perceptual_hash;
1719
1720  Image
1721    *hash_image;
1722
1723  MagickBooleanType
1724    status;
1725
1726  register ssize_t
1727    i;
1728
1729  ssize_t
1730    channel;
1731
1732  /*
1733    Blur then transform to sRGB colorspace.
1734  */
1735  hash_image=BlurImage(image,0.0,1.0,exception);
1736  if (hash_image == (Image *) NULL)
1737    return((ChannelPerceptualHash *) NULL);
1738  hash_image->depth=8;
1739  status=TransformImageColorspace(hash_image,sRGBColorspace,exception);
1740  if (status == MagickFalse)
1741    return((ChannelPerceptualHash *) NULL);
1742  moments=GetImageMoments(hash_image,exception);
1743  hash_image=DestroyImage(hash_image);
1744  if (moments == (ChannelMoments *) NULL)
1745    return((ChannelPerceptualHash *) NULL);
1746  perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
1747    CompositeChannels+1UL,sizeof(*perceptual_hash));
1748  if (perceptual_hash == (ChannelPerceptualHash *) NULL)
1749    return((ChannelPerceptualHash *) NULL);
1750  for (channel=0; channel <= MaxPixelChannels; channel++)
1751    for (i=0; i < MaximumNumberOfImageMoments; i++)
1752      perceptual_hash[channel].srgb_hu_phash[i]=
1753        (-MagickLog10(moments[channel].invariant[i]));
1754  moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1755  /*
1756    Blur then transform to HCLp colorspace.
1757  */
1758  hash_image=BlurImage(image,0.0,1.0,exception);
1759  if (hash_image == (Image *) NULL)
1760    {
1761      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1762        perceptual_hash);
1763      return((ChannelPerceptualHash *) NULL);
1764    }
1765  hash_image->depth=8;
1766  status=TransformImageColorspace(hash_image,HCLpColorspace,exception);
1767  if (status == MagickFalse)
1768    {
1769      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1770        perceptual_hash);
1771      return((ChannelPerceptualHash *) NULL);
1772    }
1773  moments=GetImageMoments(hash_image,exception);
1774  hash_image=DestroyImage(hash_image);
1775  if (moments == (ChannelMoments *) NULL)
1776    {
1777      perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1778        perceptual_hash);
1779      return((ChannelPerceptualHash *) NULL);
1780    }
1781  for (channel=0; channel <= MaxPixelChannels; channel++)
1782    for (i=0; i < MaximumNumberOfImageMoments; i++)
1783      perceptual_hash[channel].hclp_hu_phash[i]=
1784        (-MagickLog10(moments[channel].invariant[i]));
1785  moments=(ChannelMoments *) RelinquishMagickMemory(moments);
1786  return(perceptual_hash);
1787}
1788
1789/*
1790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1791%                                                                             %
1792%                                                                             %
1793%                                                                             %
1794%   G e t I m a g e R a n g e                                                 %
1795%                                                                             %
1796%                                                                             %
1797%                                                                             %
1798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1799%
1800%  GetImageRange() returns the range of one or more image channels.
1801%
1802%  The format of the GetImageRange method is:
1803%
1804%      MagickBooleanType GetImageRange(const Image *image,double *minima,
1805%        double *maxima,ExceptionInfo *exception)
1806%
1807%  A description of each parameter follows:
1808%
1809%    o image: the image.
1810%
1811%    o minima: the minimum value in the channel.
1812%
1813%    o maxima: the maximum value in the channel.
1814%
1815%    o exception: return any errors or warnings in this structure.
1816%
1817*/
1818MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1819  double *maxima,ExceptionInfo *exception)
1820{
1821  CacheView
1822    *image_view;
1823
1824  MagickBooleanType
1825    initialize,
1826    status;
1827
1828  ssize_t
1829    y;
1830
1831  assert(image != (Image *) NULL);
1832  assert(image->signature == MagickSignature);
1833  if (image->debug != MagickFalse)
1834    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1835  status=MagickTrue;
1836  initialize=MagickTrue;
1837  *maxima=0.0;
1838  *minima=0.0;
1839  image_view=AcquireVirtualCacheView(image,exception);
1840#if defined(MAGICKCORE_OPENMP_SUPPORT)
1841  #pragma omp parallel for schedule(static,4) shared(status,initialize) \
1842    magick_threads(image,image,image->rows,1)
1843#endif
1844  for (y=0; y < (ssize_t) image->rows; y++)
1845  {
1846    register const Quantum
1847      *restrict p;
1848
1849    register ssize_t
1850      x;
1851
1852    if (status == MagickFalse)
1853      continue;
1854    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1855    if (p == (const Quantum *) NULL)
1856      {
1857        status=MagickFalse;
1858        continue;
1859      }
1860    for (x=0; x < (ssize_t) image->columns; x++)
1861    {
1862      register ssize_t
1863        i;
1864
1865      if (GetPixelReadMask(image,p) == 0)
1866        {
1867          p+=GetPixelChannels(image);
1868          continue;
1869        }
1870      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1871      {
1872        PixelChannel channel=GetPixelChannelChannel(image,i);
1873        PixelTrait traits=GetPixelChannelTraits(image,channel);
1874        if (traits == UndefinedPixelTrait)
1875          continue;
1876        if ((traits & UpdatePixelTrait) == 0)
1877          continue;
1878#if defined(MAGICKCORE_OPENMP_SUPPORT)
1879        #pragma omp critical (MagickCore_GetImageRange)
1880#endif
1881        {
1882          if (initialize != MagickFalse)
1883            {
1884              *minima=(double) p[i];
1885              *maxima=(double) p[i];
1886              initialize=MagickFalse;
1887            }
1888          else
1889            {
1890              if ((double) p[i] < *minima)
1891                *minima=(double) p[i];
1892              if ((double) p[i] > *maxima)
1893                *maxima=(double) p[i];
1894           }
1895        }
1896      }
1897      p+=GetPixelChannels(image);
1898    }
1899  }
1900  image_view=DestroyCacheView(image_view);
1901  return(status);
1902}
1903
1904/*
1905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1906%                                                                             %
1907%                                                                             %
1908%                                                                             %
1909%   G e t I m a g e S t a t i s t i c s                                       %
1910%                                                                             %
1911%                                                                             %
1912%                                                                             %
1913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1914%
1915%  GetImageStatistics() returns statistics for each channel in the image.  The
1916%  statistics include the channel depth, its minima, maxima, mean, standard
1917%  deviation, kurtosis and skewness.  You can access the red channel mean, for
1918%  example, like this:
1919%
1920%      channel_statistics=GetImageStatistics(image,exception);
1921%      red_mean=channel_statistics[RedPixelChannel].mean;
1922%
1923%  Use MagickRelinquishMemory() to free the statistics buffer.
1924%
1925%  The format of the GetImageStatistics method is:
1926%
1927%      ChannelStatistics *GetImageStatistics(const Image *image,
1928%        ExceptionInfo *exception)
1929%
1930%  A description of each parameter follows:
1931%
1932%    o image: the image.
1933%
1934%    o exception: return any errors or warnings in this structure.
1935%
1936*/
1937MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
1938  ExceptionInfo *exception)
1939{
1940  ChannelStatistics
1941    *channel_statistics;
1942
1943  double
1944    *histogram;
1945
1946  MagickStatusType
1947    status;
1948
1949  QuantumAny
1950    range;
1951
1952  register ssize_t
1953    i;
1954
1955  size_t
1956    channels,
1957    depth;
1958
1959  ssize_t
1960    y;
1961
1962  assert(image != (Image *) NULL);
1963  assert(image->signature == MagickSignature);
1964  if (image->debug != MagickFalse)
1965    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1966  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1967    sizeof(*histogram));
1968  channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1969    MaxPixelChannels+1,sizeof(*channel_statistics));
1970  if ((channel_statistics == (ChannelStatistics *) NULL) ||
1971      (histogram == (double *) NULL))
1972    {
1973      if (histogram != (double *) NULL)
1974        histogram=(double *) RelinquishMagickMemory(histogram);
1975      if (channel_statistics != (ChannelStatistics *) NULL)
1976        channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1977          channel_statistics);
1978      return(channel_statistics);
1979    }
1980  (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
1981    sizeof(*channel_statistics));
1982  for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
1983  {
1984    channel_statistics[i].depth=1;
1985    channel_statistics[i].maxima=(-MagickMaximumValue);
1986    channel_statistics[i].minima=MagickMaximumValue;
1987  }
1988  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1989    sizeof(*histogram));
1990  for (y=0; y < (ssize_t) image->rows; y++)
1991  {
1992    register const Quantum
1993      *restrict p;
1994
1995    register ssize_t
1996      x;
1997
1998    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1999    if (p == (const Quantum *) NULL)
2000      break;
2001    for (x=0; x < (ssize_t) image->columns; x++)
2002    {
2003      register ssize_t
2004        i;
2005
2006      if (GetPixelReadMask(image,p) == 0)
2007        {
2008          p+=GetPixelChannels(image);
2009          continue;
2010        }
2011      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2012      {
2013        PixelChannel channel=GetPixelChannelChannel(image,i);
2014        PixelTrait traits=GetPixelChannelTraits(image,channel);
2015        if (traits == UndefinedPixelTrait)
2016          continue;
2017        if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
2018          {
2019            depth=channel_statistics[channel].depth;
2020            range=GetQuantumRange(depth);
2021            status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
2022              range) ? MagickTrue : MagickFalse;
2023            if (status != MagickFalse)
2024              {
2025                channel_statistics[channel].depth++;
2026                i--;
2027                continue;
2028              }
2029          }
2030        if ((double) p[i] < channel_statistics[channel].minima)
2031          channel_statistics[channel].minima=(double) p[i];
2032        if ((double) p[i] > channel_statistics[channel].maxima)
2033          channel_statistics[channel].maxima=(double) p[i];
2034        channel_statistics[channel].sum+=p[i];
2035        channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
2036        channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
2037        channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
2038          p[i];
2039        channel_statistics[channel].area++;
2040        histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2041          ClampToQuantum((double) p[i]))+i]++;
2042      }
2043      p+=GetPixelChannels(image);
2044    }
2045  }
2046  for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2047  {
2048    double
2049      area;
2050
2051    register ssize_t
2052      j;
2053
2054    area=PerceptibleReciprocal(channel_statistics[i].area);
2055    channel_statistics[i].sum*=area;
2056    channel_statistics[i].sum_squared*=area;
2057    channel_statistics[i].sum_cubed*=area;
2058    channel_statistics[i].sum_fourth_power*=area;
2059    channel_statistics[i].mean=channel_statistics[i].sum;
2060    channel_statistics[i].variance=channel_statistics[i].sum_squared;
2061    channel_statistics[i].standard_deviation=sqrt(
2062      channel_statistics[i].variance-(channel_statistics[i].mean*
2063      channel_statistics[i].mean));
2064    for (j=0; j < (ssize_t) (MaxMap+1U); j++)
2065    {
2066      double
2067        count;
2068
2069      count=histogram[GetPixelChannels(image)*j+i]/area;
2070      channel_statistics[i].entropy+=-count*MagickLog10(count)/
2071        MagickLog10(MaxMap+1.0);
2072    }
2073  }
2074  for (i=0; i < (ssize_t) MaxPixelChannels; i++)
2075  {
2076    channel_statistics[CompositePixelChannel].area+=channel_statistics[i].area;
2077    channel_statistics[CompositePixelChannel].minima=MagickMin(
2078      channel_statistics[CompositePixelChannel].minima,
2079      channel_statistics[i].minima);
2080    channel_statistics[CompositePixelChannel].maxima=EvaluateMax(
2081      channel_statistics[CompositePixelChannel].maxima,
2082      channel_statistics[i].maxima);
2083    channel_statistics[CompositePixelChannel].sum+=channel_statistics[i].sum;
2084    channel_statistics[CompositePixelChannel].sum_squared+=
2085      channel_statistics[i].sum_squared;
2086    channel_statistics[CompositePixelChannel].sum_cubed+=
2087      channel_statistics[i].sum_cubed;
2088    channel_statistics[CompositePixelChannel].sum_fourth_power+=
2089      channel_statistics[i].sum_fourth_power;
2090    channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
2091    channel_statistics[CompositePixelChannel].variance+=
2092      channel_statistics[i].variance-channel_statistics[i].mean*
2093      channel_statistics[i].mean;
2094    channel_statistics[CompositePixelChannel].standard_deviation+=
2095      channel_statistics[i].variance-channel_statistics[i].mean*
2096      channel_statistics[i].mean;
2097    channel_statistics[CompositePixelChannel].entropy+=
2098      channel_statistics[i].entropy;
2099  }
2100  channels=GetImageChannels(image);
2101  channel_statistics[CompositePixelChannel].area/=channels;
2102  channel_statistics[CompositePixelChannel].sum/=channels;
2103  channel_statistics[CompositePixelChannel].sum_squared/=channels;
2104  channel_statistics[CompositePixelChannel].sum_cubed/=channels;
2105  channel_statistics[CompositePixelChannel].sum_fourth_power/=channels;
2106  channel_statistics[CompositePixelChannel].mean/=channels;
2107  channel_statistics[CompositePixelChannel].variance/=channels;
2108  channel_statistics[CompositePixelChannel].standard_deviation=
2109    sqrt(channel_statistics[CompositePixelChannel].standard_deviation/channels);
2110  channel_statistics[CompositePixelChannel].kurtosis/=channels;
2111  channel_statistics[CompositePixelChannel].skewness/=channels;
2112  channel_statistics[CompositePixelChannel].entropy/=channels;
2113  for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
2114  {
2115    double
2116      standard_deviation;
2117
2118    if (channel_statistics[i].standard_deviation == 0.0)
2119      continue;
2120    standard_deviation=PerceptibleReciprocal(
2121      channel_statistics[i].standard_deviation);
2122    channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
2123      channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
2124      channel_statistics[i].mean*channel_statistics[i].mean*
2125      channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2126      standard_deviation);
2127    channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
2128      channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
2129      channel_statistics[i].mean*channel_statistics[i].mean*
2130      channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
2131      channel_statistics[i].mean*1.0*channel_statistics[i].mean*
2132      channel_statistics[i].mean)*(standard_deviation*standard_deviation*
2133      standard_deviation*standard_deviation)-3.0;
2134  }
2135  histogram=(double *) RelinquishMagickMemory(histogram);
2136  if (y < (ssize_t) image->rows)
2137    channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
2138      channel_statistics);
2139  return(channel_statistics);
2140}
2141
2142/*
2143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2144%                                                                             %
2145%                                                                             %
2146%                                                                             %
2147%     P o l y n o m i a l I m a g e                                           %
2148%                                                                             %
2149%                                                                             %
2150%                                                                             %
2151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2152%
2153%  PolynomialImage() returns a new image where each pixel is the sum of the
2154%  pixels in the image sequence after applying its corresponding terms
2155%  (coefficient and degree pairs).
2156%
2157%  The format of the PolynomialImage method is:
2158%
2159%      Image *PolynomialImage(const Image *images,const size_t number_terms,
2160%        const double *terms,ExceptionInfo *exception)
2161%
2162%  A description of each parameter follows:
2163%
2164%    o images: the image sequence.
2165%
2166%    o number_terms: the number of terms in the list.  The actual list length
2167%      is 2 x number_terms + 1 (the constant).
2168%
2169%    o terms: the list of polynomial coefficients and degree pairs and a
2170%      constant.
2171%
2172%    o exception: return any errors or warnings in this structure.
2173%
2174*/
2175
2176MagickExport Image *PolynomialImage(const Image *images,
2177  const size_t number_terms,const double *terms,ExceptionInfo *exception)
2178{
2179#define PolynomialImageTag  "Polynomial/Image"
2180
2181  CacheView
2182    *polynomial_view;
2183
2184  Image
2185    *image;
2186
2187  MagickBooleanType
2188    status;
2189
2190  MagickOffsetType
2191    progress;
2192
2193  PixelChannels
2194    **restrict polynomial_pixels;
2195
2196  size_t
2197    number_images;
2198
2199  ssize_t
2200    y;
2201
2202  assert(images != (Image *) NULL);
2203  assert(images->signature == MagickSignature);
2204  if (images->debug != MagickFalse)
2205    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2206  assert(exception != (ExceptionInfo *) NULL);
2207  assert(exception->signature == MagickSignature);
2208  image=CloneImage(images,images->columns,images->rows,MagickTrue,
2209    exception);
2210  if (image == (Image *) NULL)
2211    return((Image *) NULL);
2212  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2213    {
2214      image=DestroyImage(image);
2215      return((Image *) NULL);
2216    }
2217  number_images=GetImageListLength(images);
2218  polynomial_pixels=AcquirePixelThreadSet(images,number_images);
2219  if (polynomial_pixels == (PixelChannels **) NULL)
2220    {
2221      image=DestroyImage(image);
2222      (void) ThrowMagickException(exception,GetMagickModule(),
2223        ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2224      return((Image *) NULL);
2225    }
2226  /*
2227    Polynomial image pixels.
2228  */
2229  status=MagickTrue;
2230  progress=0;
2231  polynomial_view=AcquireAuthenticCacheView(image,exception);
2232#if defined(MAGICKCORE_OPENMP_SUPPORT)
2233  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2234    magick_threads(image,image,image->rows,1)
2235#endif
2236  for (y=0; y < (ssize_t) image->rows; y++)
2237  {
2238    CacheView
2239      *image_view;
2240
2241    const Image
2242      *next;
2243
2244    const int
2245      id = GetOpenMPThreadId();
2246
2247    register ssize_t
2248      i,
2249      x;
2250
2251    register PixelChannels
2252      *polynomial_pixel;
2253
2254    register Quantum
2255      *restrict q;
2256
2257    ssize_t
2258      j;
2259
2260    if (status == MagickFalse)
2261      continue;
2262    q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
2263      exception);
2264    if (q == (Quantum *) NULL)
2265      {
2266        status=MagickFalse;
2267        continue;
2268      }
2269    polynomial_pixel=polynomial_pixels[id];
2270    for (j=0; j < (ssize_t) image->columns; j++)
2271      for (i=0; i < MaxPixelChannels; i++)
2272        polynomial_pixel[j].channel[i]=0.0;
2273    next=images;
2274    for (j=0; j < (ssize_t) number_images; j++)
2275    {
2276      register const Quantum
2277        *p;
2278
2279      if (j >= (ssize_t) number_terms)
2280        continue;
2281      image_view=AcquireVirtualCacheView(next,exception);
2282      p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2283      if (p == (const Quantum *) NULL)
2284        {
2285          image_view=DestroyCacheView(image_view);
2286          break;
2287        }
2288      for (x=0; x < (ssize_t) image->columns; x++)
2289      {
2290        register ssize_t
2291          i;
2292
2293        if (GetPixelReadMask(next,p) == 0)
2294          {
2295            p+=GetPixelChannels(next);
2296            continue;
2297          }
2298        for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
2299        {
2300          MagickRealType
2301            coefficient,
2302            degree;
2303
2304          PixelChannel channel=GetPixelChannelChannel(image,i);
2305          PixelTrait traits=GetPixelChannelTraits(next,channel);
2306          PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
2307          if ((traits == UndefinedPixelTrait) ||
2308              (polynomial_traits == UndefinedPixelTrait))
2309            continue;
2310          if ((traits & UpdatePixelTrait) == 0)
2311            continue;
2312          coefficient=(MagickRealType) terms[2*i];
2313          degree=(MagickRealType) terms[(i << 1)+1];
2314          polynomial_pixel[x].channel[i]+=coefficient*
2315            pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
2316        }
2317        p+=GetPixelChannels(next);
2318      }
2319      image_view=DestroyCacheView(image_view);
2320      next=GetNextImageInList(next);
2321    }
2322    for (x=0; x < (ssize_t) image->columns; x++)
2323    {
2324      register ssize_t
2325        i;
2326
2327      if (GetPixelReadMask(image,q) == 0)
2328        {
2329          q+=GetPixelChannels(image);
2330          continue;
2331        }
2332      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2333      {
2334        PixelChannel channel=GetPixelChannelChannel(image,i);
2335        PixelTrait traits=GetPixelChannelTraits(image,channel);
2336        if (traits == UndefinedPixelTrait)
2337          continue;
2338        if ((traits & UpdatePixelTrait) == 0)
2339          continue;
2340        q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
2341      }
2342      q+=GetPixelChannels(image);
2343    }
2344    if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
2345      status=MagickFalse;
2346    if (images->progress_monitor != (MagickProgressMonitor) NULL)
2347      {
2348        MagickBooleanType
2349          proceed;
2350
2351#if defined(MAGICKCORE_OPENMP_SUPPORT)
2352        #pragma omp critical (MagickCore_PolynomialImages)
2353#endif
2354        proceed=SetImageProgress(images,PolynomialImageTag,progress++,
2355          image->rows);
2356        if (proceed == MagickFalse)
2357          status=MagickFalse;
2358      }
2359  }
2360  polynomial_view=DestroyCacheView(polynomial_view);
2361  polynomial_pixels=DestroyPixelThreadSet(polynomial_pixels);
2362  if (status == MagickFalse)
2363    image=DestroyImage(image);
2364  return(image);
2365}
2366
2367/*
2368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2369%                                                                             %
2370%                                                                             %
2371%                                                                             %
2372%     S t a t i s t i c I m a g e                                             %
2373%                                                                             %
2374%                                                                             %
2375%                                                                             %
2376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2377%
2378%  StatisticImage() makes each pixel the min / max / median / mode / etc. of
2379%  the neighborhood of the specified width and height.
2380%
2381%  The format of the StatisticImage method is:
2382%
2383%      Image *StatisticImage(const Image *image,const StatisticType type,
2384%        const size_t width,const size_t height,ExceptionInfo *exception)
2385%
2386%  A description of each parameter follows:
2387%
2388%    o image: the image.
2389%
2390%    o type: the statistic type (median, mode, etc.).
2391%
2392%    o width: the width of the pixel neighborhood.
2393%
2394%    o height: the height of the pixel neighborhood.
2395%
2396%    o exception: return any errors or warnings in this structure.
2397%
2398*/
2399
2400typedef struct _SkipNode
2401{
2402  size_t
2403    next[9],
2404    count,
2405    signature;
2406} SkipNode;
2407
2408typedef struct _SkipList
2409{
2410  ssize_t
2411    level;
2412
2413  SkipNode
2414    *nodes;
2415} SkipList;
2416
2417typedef struct _PixelList
2418{
2419  size_t
2420    length,
2421    seed;
2422
2423  SkipList
2424    skip_list;
2425
2426  size_t
2427    signature;
2428} PixelList;
2429
2430static PixelList *DestroyPixelList(PixelList *pixel_list)
2431{
2432  if (pixel_list == (PixelList *) NULL)
2433    return((PixelList *) NULL);
2434  if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
2435    pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
2436      pixel_list->skip_list.nodes);
2437  pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
2438  return(pixel_list);
2439}
2440
2441static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
2442{
2443  register ssize_t
2444    i;
2445
2446  assert(pixel_list != (PixelList **) NULL);
2447  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2448    if (pixel_list[i] != (PixelList *) NULL)
2449      pixel_list[i]=DestroyPixelList(pixel_list[i]);
2450  pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
2451  return(pixel_list);
2452}
2453
2454static PixelList *AcquirePixelList(const size_t width,const size_t height)
2455{
2456  PixelList
2457    *pixel_list;
2458
2459  pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
2460  if (pixel_list == (PixelList *) NULL)
2461    return(pixel_list);
2462  (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
2463  pixel_list->length=width*height;
2464  pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
2465    sizeof(*pixel_list->skip_list.nodes));
2466  if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
2467    return(DestroyPixelList(pixel_list));
2468  (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
2469    sizeof(*pixel_list->skip_list.nodes));
2470  pixel_list->signature=MagickSignature;
2471  return(pixel_list);
2472}
2473
2474static PixelList **AcquirePixelListThreadSet(const size_t width,
2475  const size_t height)
2476{
2477  PixelList
2478    **pixel_list;
2479
2480  register ssize_t
2481    i;
2482
2483  size_t
2484    number_threads;
2485
2486  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2487  pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
2488    sizeof(*pixel_list));
2489  if (pixel_list == (PixelList **) NULL)
2490    return((PixelList **) NULL);
2491  (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
2492  for (i=0; i < (ssize_t) number_threads; i++)
2493  {
2494    pixel_list[i]=AcquirePixelList(width,height);
2495    if (pixel_list[i] == (PixelList *) NULL)
2496      return(DestroyPixelListThreadSet(pixel_list));
2497  }
2498  return(pixel_list);
2499}
2500
2501static void AddNodePixelList(PixelList *pixel_list,const size_t color)
2502{
2503  register SkipList
2504    *p;
2505
2506  register ssize_t
2507    level;
2508
2509  size_t
2510    search,
2511    update[9];
2512
2513  /*
2514    Initialize the node.
2515  */
2516  p=(&pixel_list->skip_list);
2517  p->nodes[color].signature=pixel_list->signature;
2518  p->nodes[color].count=1;
2519  /*
2520    Determine where it belongs in the list.
2521  */
2522  search=65536UL;
2523  for (level=p->level; level >= 0; level--)
2524  {
2525    while (p->nodes[search].next[level] < color)
2526      search=p->nodes[search].next[level];
2527    update[level]=search;
2528  }
2529  /*
2530    Generate a pseudo-random level for this node.
2531  */
2532  for (level=0; ; level++)
2533  {
2534    pixel_list->seed=(pixel_list->seed*42893621L)+1L;
2535    if ((pixel_list->seed & 0x300) != 0x300)
2536      break;
2537  }
2538  if (level > 8)
2539    level=8;
2540  if (level > (p->level+2))
2541    level=p->level+2;
2542  /*
2543    If we're raising the list's level, link back to the root node.
2544  */
2545  while (level > p->level)
2546  {
2547    p->level++;
2548    update[p->level]=65536UL;
2549  }
2550  /*
2551    Link the node into the skip-list.
2552  */
2553  do
2554  {
2555    p->nodes[color].next[level]=p->nodes[update[level]].next[level];
2556    p->nodes[update[level]].next[level]=color;
2557  } while (level-- > 0);
2558}
2559
2560static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
2561{
2562  register SkipList
2563    *p;
2564
2565  size_t
2566    color,
2567    maximum;
2568
2569  ssize_t
2570    count;
2571
2572  /*
2573    Find the maximum value for each of the color.
2574  */
2575  p=(&pixel_list->skip_list);
2576  color=65536L;
2577  count=0;
2578  maximum=p->nodes[color].next[0];
2579  do
2580  {
2581    color=p->nodes[color].next[0];
2582    if (color > maximum)
2583      maximum=color;
2584    count+=p->nodes[color].count;
2585  } while (count < (ssize_t) pixel_list->length);
2586  *pixel=ScaleShortToQuantum((unsigned short) maximum);
2587}
2588
2589static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
2590{
2591  double
2592    sum;
2593
2594  register SkipList
2595    *p;
2596
2597  size_t
2598    color;
2599
2600  ssize_t
2601    count;
2602
2603  /*
2604    Find the mean value for each of the color.
2605  */
2606  p=(&pixel_list->skip_list);
2607  color=65536L;
2608  count=0;
2609  sum=0.0;
2610  do
2611  {
2612    color=p->nodes[color].next[0];
2613    sum+=(double) p->nodes[color].count*color;
2614    count+=p->nodes[color].count;
2615  } while (count < (ssize_t) pixel_list->length);
2616  sum/=pixel_list->length;
2617  *pixel=ScaleShortToQuantum((unsigned short) sum);
2618}
2619
2620static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
2621{
2622  register SkipList
2623    *p;
2624
2625  size_t
2626    color;
2627
2628  ssize_t
2629    count;
2630
2631  /*
2632    Find the median value for each of the color.
2633  */
2634  p=(&pixel_list->skip_list);
2635  color=65536L;
2636  count=0;
2637  do
2638  {
2639    color=p->nodes[color].next[0];
2640    count+=p->nodes[color].count;
2641  } while (count <= (ssize_t) (pixel_list->length >> 1));
2642  *pixel=ScaleShortToQuantum((unsigned short) color);
2643}
2644
2645static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
2646{
2647  register SkipList
2648    *p;
2649
2650  size_t
2651    color,
2652    minimum;
2653
2654  ssize_t
2655    count;
2656
2657  /*
2658    Find the minimum value for each of the color.
2659  */
2660  p=(&pixel_list->skip_list);
2661  count=0;
2662  color=65536UL;
2663  minimum=p->nodes[color].next[0];
2664  do
2665  {
2666    color=p->nodes[color].next[0];
2667    if (color < minimum)
2668      minimum=color;
2669    count+=p->nodes[color].count;
2670  } while (count < (ssize_t) pixel_list->length);
2671  *pixel=ScaleShortToQuantum((unsigned short) minimum);
2672}
2673
2674static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
2675{
2676  register SkipList
2677    *p;
2678
2679  size_t
2680    color,
2681    max_count,
2682    mode;
2683
2684  ssize_t
2685    count;
2686
2687  /*
2688    Make each pixel the 'predominant color' of the specified neighborhood.
2689  */
2690  p=(&pixel_list->skip_list);
2691  color=65536L;
2692  mode=color;
2693  max_count=p->nodes[mode].count;
2694  count=0;
2695  do
2696  {
2697    color=p->nodes[color].next[0];
2698    if (p->nodes[color].count > max_count)
2699      {
2700        mode=color;
2701        max_count=p->nodes[mode].count;
2702      }
2703    count+=p->nodes[color].count;
2704  } while (count < (ssize_t) pixel_list->length);
2705  *pixel=ScaleShortToQuantum((unsigned short) mode);
2706}
2707
2708static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2709{
2710  register SkipList
2711    *p;
2712
2713  size_t
2714    color,
2715    next,
2716    previous;
2717
2718  ssize_t
2719    count;
2720
2721  /*
2722    Finds the non peak value for each of the colors.
2723  */
2724  p=(&pixel_list->skip_list);
2725  color=65536L;
2726  next=p->nodes[color].next[0];
2727  count=0;
2728  do
2729  {
2730    previous=color;
2731    color=next;
2732    next=p->nodes[color].next[0];
2733    count+=p->nodes[color].count;
2734  } while (count <= (ssize_t) (pixel_list->length >> 1));
2735  if ((previous == 65536UL) && (next != 65536UL))
2736    color=next;
2737  else
2738    if ((previous != 65536UL) && (next == 65536UL))
2739      color=previous;
2740  *pixel=ScaleShortToQuantum((unsigned short) color);
2741}
2742
2743static inline void GetRootMeanSquarePixelList(PixelList *pixel_list,
2744  Quantum *pixel)
2745{
2746  double
2747    sum;
2748
2749  register SkipList
2750    *p;
2751
2752  size_t
2753    color;
2754
2755  ssize_t
2756    count;
2757
2758  /*
2759    Find the root mean square value for each of the color.
2760  */
2761  p=(&pixel_list->skip_list);
2762  color=65536L;
2763  count=0;
2764  sum=0.0;
2765  do
2766  {
2767    color=p->nodes[color].next[0];
2768    sum+=(double) (p->nodes[color].count*color*color);
2769    count+=p->nodes[color].count;
2770  } while (count < (ssize_t) pixel_list->length);
2771  sum/=pixel_list->length;
2772  *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum));
2773}
2774
2775static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2776  Quantum *pixel)
2777{
2778  double
2779    sum,
2780    sum_squared;
2781
2782  register SkipList
2783    *p;
2784
2785  size_t
2786    color;
2787
2788  ssize_t
2789    count;
2790
2791  /*
2792    Find the standard-deviation value for each of the color.
2793  */
2794  p=(&pixel_list->skip_list);
2795  color=65536L;
2796  count=0;
2797  sum=0.0;
2798  sum_squared=0.0;
2799  do
2800  {
2801    register ssize_t
2802      i;
2803
2804    color=p->nodes[color].next[0];
2805    sum+=(double) p->nodes[color].count*color;
2806    for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2807      sum_squared+=((double) color)*((double) color);
2808    count+=p->nodes[color].count;
2809  } while (count < (ssize_t) pixel_list->length);
2810  sum/=pixel_list->length;
2811  sum_squared/=pixel_list->length;
2812  *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2813}
2814
2815static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
2816{
2817  size_t
2818    signature;
2819
2820  unsigned short
2821    index;
2822
2823  index=ScaleQuantumToShort(pixel);
2824  signature=pixel_list->skip_list.nodes[index].signature;
2825  if (signature == pixel_list->signature)
2826    {
2827      pixel_list->skip_list.nodes[index].count++;
2828      return;
2829    }
2830  AddNodePixelList(pixel_list,index);
2831}
2832
2833static inline double MagickAbsoluteValue(const double x)
2834{
2835  if (x < 0)
2836    return(-x);
2837  return(x);
2838}
2839
2840static inline size_t MagickMax(const size_t x,const size_t y)
2841{
2842  if (x > y)
2843    return(x);
2844  return(y);
2845}
2846
2847static void ResetPixelList(PixelList *pixel_list)
2848{
2849  int
2850    level;
2851
2852  register SkipNode
2853    *root;
2854
2855  register SkipList
2856    *p;
2857
2858  /*
2859    Reset the skip-list.
2860  */
2861  p=(&pixel_list->skip_list);
2862  root=p->nodes+65536UL;
2863  p->level=0;
2864  for (level=0; level < 9; level++)
2865    root->next[level]=65536UL;
2866  pixel_list->seed=pixel_list->signature++;
2867}
2868
2869MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2870  const size_t width,const size_t height,ExceptionInfo *exception)
2871{
2872#define StatisticImageTag  "Statistic/Image"
2873
2874  CacheView
2875    *image_view,
2876    *statistic_view;
2877
2878  Image
2879    *statistic_image;
2880
2881  MagickBooleanType
2882    status;
2883
2884  MagickOffsetType
2885    progress;
2886
2887  PixelList
2888    **restrict pixel_list;
2889
2890  ssize_t
2891    center,
2892    y;
2893
2894  /*
2895    Initialize statistics image attributes.
2896  */
2897  assert(image != (Image *) NULL);
2898  assert(image->signature == MagickSignature);
2899  if (image->debug != MagickFalse)
2900    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2901  assert(exception != (ExceptionInfo *) NULL);
2902  assert(exception->signature == MagickSignature);
2903  statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2904    exception);
2905  if (statistic_image == (Image *) NULL)
2906    return((Image *) NULL);
2907  status=SetImageStorageClass(statistic_image,DirectClass,exception);
2908  if (status == MagickFalse)
2909    {
2910      statistic_image=DestroyImage(statistic_image);
2911      return((Image *) NULL);
2912    }
2913  pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
2914  if (pixel_list == (PixelList **) NULL)
2915    {
2916      statistic_image=DestroyImage(statistic_image);
2917      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2918    }
2919  /*
2920    Make each pixel the min / max / median / mode / etc. of the neighborhood.
2921  */
2922  center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
2923    (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
2924  status=MagickTrue;
2925  progress=0;
2926  image_view=AcquireVirtualCacheView(image,exception);
2927  statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
2928#if defined(MAGICKCORE_OPENMP_SUPPORT)
2929  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2930    magick_threads(image,statistic_image,statistic_image->rows,1)
2931#endif
2932  for (y=0; y < (ssize_t) statistic_image->rows; y++)
2933  {
2934    const int
2935      id = GetOpenMPThreadId();
2936
2937    register const Quantum
2938      *restrict p;
2939
2940    register Quantum
2941      *restrict q;
2942
2943    register ssize_t
2944      x;
2945
2946    if (status == MagickFalse)
2947      continue;
2948    p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
2949      (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
2950      MagickMax(height,1),exception);
2951    q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
2952    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2953      {
2954        status=MagickFalse;
2955        continue;
2956      }
2957    for (x=0; x < (ssize_t) statistic_image->columns; x++)
2958    {
2959      register ssize_t
2960        i;
2961
2962      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2963      {
2964        Quantum
2965          pixel;
2966
2967        register const Quantum
2968          *restrict pixels;
2969
2970        register ssize_t
2971          u;
2972
2973        ssize_t
2974          v;
2975
2976        PixelChannel channel=GetPixelChannelChannel(image,i);
2977        PixelTrait traits=GetPixelChannelTraits(image,channel);
2978        PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
2979          channel);
2980        if ((traits == UndefinedPixelTrait) ||
2981            (statistic_traits == UndefinedPixelTrait))
2982          continue;
2983        if (((statistic_traits & CopyPixelTrait) != 0) ||
2984            (GetPixelReadMask(image,p) == 0))
2985          {
2986            SetPixelChannel(statistic_image,channel,p[center+i],q);
2987            continue;
2988          }
2989        pixels=p;
2990        ResetPixelList(pixel_list[id]);
2991        for (v=0; v < (ssize_t) MagickMax(height,1); v++)
2992        {
2993          for (u=0; u < (ssize_t) MagickMax(width,1); u++)
2994          {
2995            InsertPixelList(pixels[i],pixel_list[id]);
2996            pixels+=GetPixelChannels(image);
2997          }
2998          pixels+=(image->columns-1)*GetPixelChannels(image);
2999        }
3000        switch (type)
3001        {
3002          case GradientStatistic:
3003          {
3004            double
3005              maximum,
3006              minimum;
3007
3008            GetMinimumPixelList(pixel_list[id],&pixel);
3009            minimum=(double) pixel;
3010            GetMaximumPixelList(pixel_list[id],&pixel);
3011            maximum=(double) pixel;
3012            pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
3013            break;
3014          }
3015          case MaximumStatistic:
3016          {
3017            GetMaximumPixelList(pixel_list[id],&pixel);
3018            break;
3019          }
3020          case MeanStatistic:
3021          {
3022            GetMeanPixelList(pixel_list[id],&pixel);
3023            break;
3024          }
3025          case MedianStatistic:
3026          default:
3027          {
3028            GetMedianPixelList(pixel_list[id],&pixel);
3029            break;
3030          }
3031          case MinimumStatistic:
3032          {
3033            GetMinimumPixelList(pixel_list[id],&pixel);
3034            break;
3035          }
3036          case ModeStatistic:
3037          {
3038            GetModePixelList(pixel_list[id],&pixel);
3039            break;
3040          }
3041          case NonpeakStatistic:
3042          {
3043            GetNonpeakPixelList(pixel_list[id],&pixel);
3044            break;
3045          }
3046          case RootMeanSquareStatistic:
3047          {
3048            GetRootMeanSquarePixelList(pixel_list[id],&pixel);
3049            break;
3050          }
3051          case StandardDeviationStatistic:
3052          {
3053            GetStandardDeviationPixelList(pixel_list[id],&pixel);
3054            break;
3055          }
3056        }
3057        SetPixelChannel(statistic_image,channel,pixel,q);
3058      }
3059      p+=GetPixelChannels(image);
3060      q+=GetPixelChannels(statistic_image);
3061    }
3062    if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
3063      status=MagickFalse;
3064    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3065      {
3066        MagickBooleanType
3067          proceed;
3068
3069#if defined(MAGICKCORE_OPENMP_SUPPORT)
3070        #pragma omp critical (MagickCore_StatisticImage)
3071#endif
3072        proceed=SetImageProgress(image,StatisticImageTag,progress++,
3073          image->rows);
3074        if (proceed == MagickFalse)
3075          status=MagickFalse;
3076      }
3077  }
3078  statistic_view=DestroyCacheView(statistic_view);
3079  image_view=DestroyCacheView(image_view);
3080  pixel_list=DestroyPixelListThreadSet(pixel_list);
3081  if (status == MagickFalse)
3082    statistic_image=DestroyImage(statistic_image);
3083  return(statistic_image);
3084}
3085