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