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