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