]> granicus.if.org Git - imagemagick/blob - magick/statistic.c
(no commit message)
[imagemagick] / magick / statistic.c
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 Methods                           %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/animate.h"
46 #include "magick/blob.h"
47 #include "magick/blob-private.h"
48 #include "magick/cache.h"
49 #include "magick/cache-private.h"
50 #include "magick/cache-view.h"
51 #include "magick/client.h"
52 #include "magick/color.h"
53 #include "magick/color-private.h"
54 #include "magick/colorspace.h"
55 #include "magick/colorspace-private.h"
56 #include "magick/composite.h"
57 #include "magick/composite-private.h"
58 #include "magick/compress.h"
59 #include "magick/constitute.h"
60 #include "magick/deprecate.h"
61 #include "magick/display.h"
62 #include "magick/draw.h"
63 #include "magick/enhance.h"
64 #include "magick/exception.h"
65 #include "magick/exception-private.h"
66 #include "magick/gem.h"
67 #include "magick/geometry.h"
68 #include "magick/list.h"
69 #include "magick/image-private.h"
70 #include "magick/magic.h"
71 #include "magick/magick.h"
72 #include "magick/memory_.h"
73 #include "magick/module.h"
74 #include "magick/monitor.h"
75 #include "magick/option.h"
76 #include "magick/paint.h"
77 #include "magick/pixel-private.h"
78 #include "magick/profile.h"
79 #include "magick/quantize.h"
80 #include "magick/random_.h"
81 #include "magick/segment.h"
82 #include "magick/semaphore.h"
83 #include "magick/signature-private.h"
84 #include "magick/statistic.h"
85 #include "magick/string_.h"
86 #include "magick/thread-private.h"
87 #include "magick/timer.h"
88 #include "magick/utility.h"
89 #include "magick/version.h"
90 \f
91 /*
92 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 %                                                                             %
94 %                                                                             %
95 %                                                                             %
96 %     A v e r a g e I m a g e s                                               %
97 %                                                                             %
98 %                                                                             %
99 %                                                                             %
100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101 %
102 %  AverageImages() takes a set of images and averages them together.  Each
103 %  image in the set must have the same width and height.  AverageImages()
104 %  returns a single image with each corresponding pixel component of each
105 %  image averaged.   On failure, a NULL image is returned and exception
106 %  describes the reason for the failure.
107 %
108 %  The format of the AverageImages method is:
109 %
110 %      Image *AverageImages(Image *image,ExceptionInfo *exception)
111 %
112 %  A description of each parameter follows:
113 %
114 %    o image: the image sequence.
115 %
116 %    o exception: return any errors or warnings in this structure.
117 %
118 */
119
120 static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
121 {
122   register long
123     i;
124
125   assert(pixels != (MagickPixelPacket **) NULL);
126   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
127     if (pixels[i] != (MagickPixelPacket *) NULL)
128       pixels[i]=(MagickPixelPacket *) RelinquishMagickMemory(pixels[i]);
129   pixels=(MagickPixelPacket **) RelinquishAlignedMemory(pixels);
130   return(pixels);
131 }
132
133 static MagickPixelPacket **AcquirePixelThreadSet(const Image *image)
134 {
135   register long
136     i,
137     j;
138
139   MagickPixelPacket
140     **pixels;
141
142   unsigned long
143     number_threads;
144
145   number_threads=GetOpenMPMaximumThreads();
146   pixels=(MagickPixelPacket **) AcquireAlignedMemory(number_threads,
147     sizeof(*pixels));
148   if (pixels == (MagickPixelPacket **) NULL)
149     return((MagickPixelPacket **) NULL);
150   (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
151   for (i=0; i < (long) number_threads; i++)
152   {
153     pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(image->columns,
154       sizeof(**pixels));
155     if (pixels[i] == (MagickPixelPacket *) NULL)
156       return(DestroyPixelThreadSet(pixels));
157     for (j=0; j < (long) image->columns; j++)
158       GetMagickPixelPacket(image,&pixels[i][j]);
159   }
160   return(pixels);
161 }
162
163 MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
164 {
165 #define AverageImageTag  "Average/Image"
166
167   CacheView
168     *average_view;
169
170   const Image
171     *next;
172
173   Image
174     *average_image;
175
176   long
177     progress,
178     y;
179
180   MagickBooleanType
181     status;
182
183   MagickPixelPacket
184     **average_pixels,
185     zero;
186
187   unsigned long
188     number_images;
189
190   /*
191     Ensure the image are the same size.
192   */
193   assert(image != (Image *) NULL);
194   assert(image->signature == MagickSignature);
195   if (image->debug != MagickFalse)
196     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
197   assert(exception != (ExceptionInfo *) NULL);
198   assert(exception->signature == MagickSignature);
199   for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
200     if ((next->columns != image->columns) || (next->rows != image->rows))
201       ThrowImageException(OptionError,"ImageWidthsOrHeightsDiffer");
202   /*
203     Initialize average next attributes.
204   */
205   average_image=CloneImage(image,image->columns,image->rows,MagickTrue,
206     exception);
207   if (average_image == (Image *) NULL)
208     return((Image *) NULL);
209   if (SetImageStorageClass(average_image,DirectClass) == MagickFalse)
210     {
211       InheritException(exception,&average_image->exception);
212       average_image=DestroyImage(average_image);
213       return((Image *) NULL);
214     }
215   average_pixels=AcquirePixelThreadSet(image);
216   if (average_pixels == (MagickPixelPacket **) NULL)
217     {
218       average_image=DestroyImage(average_image);
219       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
220     }
221   /*
222     Average image pixels.
223   */
224   status=MagickTrue;
225   progress=0;
226   GetMagickPixelPacket(image,&zero);
227   number_images=GetImageListLength(image);
228   average_view=AcquireCacheView(average_image);
229 #if defined(_OPENMP) && (_OPENMP >= 200203)
230   #pragma omp parallel for schedule(static,1) shared(progress,status)
231 #endif
232   for (y=0; y < (long) average_image->rows; y++)
233   {
234     CacheView
235       *image_view;
236
237     const Image
238       *next;
239
240     MagickPixelPacket
241       pixel;
242
243     register IndexPacket
244       *__restrict average_indexes;
245
246     register long
247       i,
248       id,
249       x;
250
251     register MagickPixelPacket
252       *average_pixel;
253
254     register PixelPacket
255       *__restrict q;
256
257     if (status == MagickFalse)
258       continue;
259     q=QueueCacheViewAuthenticPixels(average_view,0,y,average_image->columns,1,
260       exception);
261     if (q == (PixelPacket *) NULL)
262       {
263         status=MagickFalse;
264         continue;
265       }
266     average_indexes=GetCacheViewAuthenticIndexQueue(average_view);
267     pixel=zero;
268     id=GetOpenMPThreadId();
269     average_pixel=average_pixels[id];
270     for (x=0; x < (long) average_image->columns; x++)
271       average_pixel[x]=zero;
272     next=image;
273     for (i=0; i < (long) number_images; i++)
274     {
275       register const IndexPacket
276         *indexes;
277
278       register const PixelPacket
279         *p;
280
281       image_view=AcquireCacheView(next);
282       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
283       if (p == (const PixelPacket *) NULL)
284         {
285           image_view=DestroyCacheView(image_view);
286           break;
287         }
288       indexes=GetCacheViewVirtualIndexQueue(image_view);
289       for (x=0; x < (long) next->columns; x++)
290       {
291         SetMagickPixelPacket(next,p,indexes+x,&pixel);
292         average_pixel[x].red+=QuantumScale*pixel.red;
293         average_pixel[x].green+=QuantumScale*pixel.green;
294         average_pixel[x].blue+=QuantumScale*pixel.blue;
295         average_pixel[x].opacity+=QuantumScale*pixel.opacity;
296         if (average_image->colorspace == CMYKColorspace)
297           average_pixel[x].index+=QuantumScale*pixel.index;
298         p++;
299       }
300       image_view=DestroyCacheView(image_view);
301       next=GetNextImageInList(next);
302     }
303     for (x=0; x < (long) average_image->columns; x++)
304     {
305       average_pixel[x].red=(MagickRealType) (QuantumRange*
306         average_pixel[x].red/number_images);
307       average_pixel[x].green=(MagickRealType) (QuantumRange*
308         average_pixel[x].green/number_images);
309       average_pixel[x].blue=(MagickRealType) (QuantumRange*
310         average_pixel[x].blue/number_images);
311       average_pixel[x].opacity=(MagickRealType) (QuantumRange*
312         average_pixel[x].opacity/number_images);
313       if (average_image->colorspace == CMYKColorspace)
314         average_pixel[x].index=(MagickRealType) (QuantumRange*
315           average_pixel[x].index/number_images);
316       SetPixelPacket(average_image,&average_pixel[x],q,average_indexes+x);
317       q++;
318     }
319     if (SyncCacheViewAuthenticPixels(average_view,exception) == MagickFalse)
320       status=MagickFalse;
321     if (image->progress_monitor != (MagickProgressMonitor) NULL)
322       {
323         MagickBooleanType
324           proceed;
325
326 #if defined(_OPENMP) && (_OPENMP >= 200203)
327         #pragma omp critical (MagickCore_AverageImages)
328 #endif
329         proceed=SetImageProgress(image,AverageImageTag,progress++,
330           average_image->rows);
331         if (proceed == MagickFalse)
332           status=MagickFalse;
333       }
334   }
335   average_view=DestroyCacheView(average_view);
336   average_pixels=DestroyPixelThreadSet(average_pixels);
337   if (status == MagickFalse)
338     average_image=DestroyImage(average_image);
339   return(average_image);
340 }
341 \f
342 /*
343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344 %                                                                             %
345 %                                                                             %
346 %                                                                             %
347 +   G e t I m a g e B o u n d i n g B o x                                     %
348 %                                                                             %
349 %                                                                             %
350 %                                                                             %
351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
352 %
353 %  GetImageBoundingBox() returns the bounding box of an image canvas.
354 %
355 %  The format of the GetImageBoundingBox method is:
356 %
357 %      RectangleInfo GetImageBoundingBox(const Image *image,
358 %        ExceptionInfo *exception)
359 %
360 %  A description of each parameter follows:
361 %
362 %    o bounds: Method GetImageBoundingBox returns the bounding box of an
363 %      image canvas.
364 %
365 %    o image: the image.
366 %
367 %    o exception: return any errors or warnings in this structure.
368 %
369 */
370 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
371   ExceptionInfo *exception)
372 {
373   long
374     y;
375
376   MagickBooleanType
377     status;
378
379   MagickPixelPacket
380     target[3],
381     zero;
382
383   RectangleInfo
384     bounds;
385
386   register const PixelPacket
387     *p;
388
389   CacheView
390     *image_view;
391
392   assert(image != (Image *) NULL);
393   assert(image->signature == MagickSignature);
394   if (image->debug != MagickFalse)
395     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
396   bounds.width=0;
397   bounds.height=0;
398   bounds.x=(long) image->columns;
399   bounds.y=(long) image->rows;
400   GetMagickPixelPacket(image,&target[0]);
401   image_view=AcquireCacheView(image);
402   p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
403   if (p == (const PixelPacket *) NULL)
404     {
405       image_view=DestroyCacheView(image_view);
406       return(bounds);
407     }
408   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
409     &target[0]);
410   GetMagickPixelPacket(image,&target[1]);
411   p=GetCacheViewVirtualPixels(image_view,(long) image->columns-1,0,1,1,
412     exception);
413   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
414     &target[1]);
415   GetMagickPixelPacket(image,&target[2]);
416   p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-1,1,1,exception);
417   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
418     &target[2]);
419   status=MagickTrue;
420   GetMagickPixelPacket(image,&zero);
421 #if defined(_OPENMP) && (_OPENMP >= 200203)
422   #pragma omp parallel for schedule(static,1) shared(status)
423 #endif
424   for (y=0; y < (long) image->rows; y++)
425   {
426     MagickPixelPacket
427       pixel;
428
429     RectangleInfo
430       bounding_box;
431
432     register const IndexPacket
433       *__restrict indexes;
434
435     register const PixelPacket
436       *__restrict p;
437
438     register long
439       x;
440
441     if (status == MagickFalse)
442       continue;
443 #if defined(HAVE_OPENMP)
444 #  pragma omp critical (MagickCore_GetImageBoundingBox)
445 #endif
446     bounding_box=bounds;
447     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
448     if (p == (const PixelPacket *) NULL)
449       {
450         status=MagickFalse;
451         continue;
452       }
453     indexes=GetCacheViewVirtualIndexQueue(image_view);
454     pixel=zero;
455     for (x=0; x < (long) image->columns; x++)
456     {
457       SetMagickPixelPacket(image,p,indexes+x,&pixel);
458       if ((x < bounding_box.x) &&
459           (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
460         bounding_box.x=x;
461       if ((x > (long) bounding_box.width) &&
462           (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
463         bounding_box.width=(unsigned long) x;
464       if ((y < bounding_box.y) &&
465           (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
466         bounding_box.y=y;
467       if ((y > (long) bounding_box.height) &&
468           (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
469         bounding_box.height=(unsigned long) y;
470       p++;
471     }
472 #if defined(HAVE_OPENMP)
473 #  pragma omp critical (MagickCore_GetImageBoundingBox)
474 #endif
475     {
476       if (bounding_box.x < bounds.x)
477         bounds.x=bounding_box.x;
478       if (bounding_box.y < bounds.y)
479         bounds.y=bounding_box.y;
480       if (bounding_box.width > bounds.width)
481         bounds.width=bounding_box.width;
482       if (bounding_box.height > bounds.height)
483         bounds.height=bounding_box.height;
484     }
485   }
486   image_view=DestroyCacheView(image_view);
487   if ((bounds.width == 0) || (bounds.height == 0))
488     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
489       "GeometryDoesNotContainImage","`%s'",image->filename);
490   else
491     {
492       bounds.width-=(bounds.x-1);
493       bounds.height-=(bounds.y-1);
494     }
495   return(bounds);
496 }
497 \f
498 /*
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 %                                                                             %
501 %                                                                             %
502 %                                                                             %
503 %   G e t I m a g e C h a n n e l D e p t h                                   %
504 %                                                                             %
505 %                                                                             %
506 %                                                                             %
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508 %
509 %  GetImageChannelDepth() returns the depth of a particular image channel.
510 %
511 %  The format of the GetImageChannelDepth method is:
512 %
513 %      unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception)
514 %      unsigned long GetImageChannelDepth(const Image *image,
515 %        const ChannelType channel,ExceptionInfo *exception)
516 %
517 %  A description of each parameter follows:
518 %
519 %    o image: the image.
520 %
521 %    o channel: the channel.
522 %
523 %    o exception: return any errors or warnings in this structure.
524 %
525 */
526
527 MagickExport unsigned long GetImageDepth(const Image *image,
528   ExceptionInfo *exception)
529 {
530   return(GetImageChannelDepth(image,AllChannels,exception));
531 }
532
533 MagickExport unsigned long GetImageChannelDepth(const Image *image,
534   const ChannelType channel,ExceptionInfo *exception)
535 {
536   long
537     y;
538
539   MagickBooleanType
540     status;
541
542   register long
543     id;
544
545   unsigned long
546     *current_depth,
547     depth,
548     number_threads;
549
550   CacheView
551     *image_view;
552
553   /*
554     Compute image depth.
555   */
556   assert(image != (Image *) NULL);
557   assert(image->signature == MagickSignature);
558   if (image->debug != MagickFalse)
559     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
560   number_threads=GetOpenMPMaximumThreads();
561   current_depth=(unsigned long *) AcquireQuantumMemory(number_threads,
562     sizeof(*current_depth));
563   if (current_depth == (unsigned long *) NULL)
564     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
565   status=MagickTrue;
566   for (id=0; id < (long) number_threads; id++)
567     current_depth[id]=1;
568   if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
569     {
570       register const PixelPacket
571         *__restrict p;
572
573       register long
574         i;
575
576       p=image->colormap;
577 #if defined(_OPENMP) && (_OPENMP >= 200203)
578   #pragma omp parallel for schedule(static,1) shared(status)
579 #endif
580       for (i=0; i < (long) image->colors; i++)
581       {
582         if (status == MagickFalse)
583           continue;
584         id=GetOpenMPThreadId();
585         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
586         {
587           MagickStatusType
588             status;
589
590           QuantumAny
591             range;
592
593           status=0;
594           range=GetQuantumRange(current_depth[id]);
595           if ((channel & RedChannel) != 0)
596             status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,
597               range),range);
598           if ((channel & GreenChannel) != 0)
599             status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
600               range),range);
601           if ((channel & BlueChannel) != 0)
602             status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
603               range),range);
604           if (status == 0)
605             break;
606           current_depth[id]++;
607         }
608         p++;
609       }
610       depth=current_depth[0];
611       for (id=1; id < (long) number_threads; id++)
612         if (depth < current_depth[id])
613           depth=current_depth[id];
614       current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
615       return(depth);
616     }
617   image_view=AcquireCacheView(image);
618 #if defined(_OPENMP) && (_OPENMP >= 200203)
619   #pragma omp parallel for schedule(static,1) shared(status)
620 #endif
621   for (y=0; y < (long) image->rows; y++)
622   {
623     register const IndexPacket
624       *__restrict indexes;
625
626     register const PixelPacket
627       *__restrict p;
628
629     register long
630       id,
631       x;
632
633     if (status == MagickFalse)
634       continue;
635     id=GetOpenMPThreadId();
636     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
637     if (p == (const PixelPacket *) NULL)
638       continue;
639     indexes=GetCacheViewVirtualIndexQueue(image_view);
640     for (x=0; x < (long) image->columns; x++)
641     {
642       while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
643       {
644         MagickStatusType
645           status;
646
647         QuantumAny
648           range;
649
650         status=0;
651         range=GetQuantumRange(current_depth[id]);
652         if ((channel & RedChannel) != 0)
653           status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
654             range);
655         if ((channel & GreenChannel) != 0)
656           status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
657             range),range);
658         if ((channel & BlueChannel) != 0)
659           status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),
660             range);
661         if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
662           status|=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,
663             range),range);
664         if (((channel & IndexChannel) != 0) &&
665             (image->colorspace == CMYKColorspace))
666           status|=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],
667             range),range);
668         if (status == 0)
669           break;
670         current_depth[id]++;
671       }
672       p++;
673     }
674     if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
675       status=MagickFalse;
676   }
677   image_view=DestroyCacheView(image_view);
678   depth=current_depth[0];
679   for (id=1; id < (long) number_threads; id++)
680     if (depth < current_depth[id])
681       depth=current_depth[id];
682   current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
683   return(depth);
684 }
685 \f
686 /*
687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688 %                                                                             %
689 %                                                                             %
690 %                                                                             %
691 +   G e t I m a g e C h a n n e l E x t r e m a                               %
692 %                                                                             %
693 %                                                                             %
694 %                                                                             %
695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696 %
697 %  GetImageChannelExtrema() returns the extrema of one or more image channels.
698 %
699 %  The format of the GetImageChannelExtrema method is:
700 %
701 %      MagickBooleanType GetImageChannelExtrema(const Image *image,
702 %        const ChannelType channel,unsigned long *minima,unsigned long *maxima,
703 %        ExceptionInfo *exception)
704 %
705 %  A description of each parameter follows:
706 %
707 %    o image: the image.
708 %
709 %    o channel: the channel.
710 %
711 %    o minima: the minimum value in the channel.
712 %
713 %    o maxima: the maximum value in the channel.
714 %
715 %    o exception: return any errors or warnings in this structure.
716 %
717 */
718
719 MagickExport MagickBooleanType GetImageExtrema(const Image *image,
720   unsigned long *minima,unsigned long *maxima,ExceptionInfo *exception)
721 {
722   return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
723 }
724
725 MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
726   const ChannelType channel,unsigned long *minima,unsigned long *maxima,
727   ExceptionInfo *exception)
728 {
729   double
730     max,
731     min;
732
733   MagickBooleanType
734     status;
735
736   assert(image != (Image *) NULL);
737   assert(image->signature == MagickSignature);
738   if (image->debug != MagickFalse)
739     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
740   status=GetImageChannelRange(image,channel,&min,&max,exception);
741   *minima=(unsigned long) (min+0.5);
742   *maxima=(unsigned long) (max+0.5);
743   return(status);
744 }
745 \f
746 /*
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 %                                                                             %
749 %                                                                             %
750 %                                                                             %
751 %   G e t I m a g e C h a n n e l M e a n                                     %
752 %                                                                             %
753 %                                                                             %
754 %                                                                             %
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756 %
757 %  GetImageChannelMean() returns the mean and standard deviation of one or more
758 %  image channels.
759 %
760 %  The format of the GetImageChannelMean method is:
761 %
762 %      MagickBooleanType GetImageChannelMean(const Image *image,
763 %        const ChannelType channel,double *mean,double *standard_deviation,
764 %        ExceptionInfo *exception)
765 %
766 %  A description of each parameter follows:
767 %
768 %    o image: the image.
769 %
770 %    o channel: the channel.
771 %
772 %    o mean: the average value in the channel.
773 %
774 %    o standard_deviation: the standard deviation of the channel.
775 %
776 %    o exception: return any errors or warnings in this structure.
777 %
778 */
779
780 MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
781   double *standard_deviation,ExceptionInfo *exception)
782 {
783   MagickBooleanType
784     status;
785
786   status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
787     exception);
788   return(status);
789 }
790
791 MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
792   const ChannelType channel,double *mean,double *standard_deviation,
793   ExceptionInfo *exception)
794 {
795   double
796     area;
797
798   long
799     y;
800
801   assert(image != (Image *) NULL);
802   assert(image->signature == MagickSignature);
803   if (image->debug != MagickFalse)
804     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
805   *mean=0.0;
806   *standard_deviation=0.0;
807   area=0.0;
808   for (y=0; y < (long) image->rows; y++)
809   {
810     register const IndexPacket
811       *__restrict indexes;
812
813     register const PixelPacket
814       *__restrict p;
815
816     register long
817       x;
818
819     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
820     if (p == (const PixelPacket *) NULL)
821       break;
822     indexes=GetVirtualIndexQueue(image);
823     for (x=0; x < (long) image->columns; x++)
824     {
825       if ((channel & RedChannel) != 0)
826         {
827           *mean+=p->red;
828           *standard_deviation+=(double) p->red*p->red;
829           area++;
830         }
831       if ((channel & GreenChannel) != 0)
832         {
833           *mean+=p->green;
834           *standard_deviation+=(double) p->green*p->green;
835           area++;
836         }
837       if ((channel & BlueChannel) != 0)
838         {
839           *mean+=p->blue;
840           *standard_deviation+=(double) p->blue*p->blue;
841           area++;
842         }
843       if ((channel & OpacityChannel) != 0)
844         {
845           *mean+=p->opacity;
846           *standard_deviation+=(double) p->opacity*p->opacity;
847           area++;
848         }
849       if (((channel & IndexChannel) != 0) &&
850           (image->colorspace == CMYKColorspace))
851         {
852           *mean+=indexes[x];
853           *standard_deviation+=(double) indexes[x]*indexes[x];
854           area++;
855         }
856       p++;
857     }
858   }
859   if (y < (long) image->rows)
860     return(MagickFalse);
861   if (area != 0)
862     {
863       *mean/=area;
864       *standard_deviation/=area;
865     }
866   *standard_deviation=sqrt(*standard_deviation-(*mean*(*mean)));
867   return(y == (long) image->rows ? MagickTrue : MagickFalse);
868 }
869 \f
870 /*
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %                                                                             %
873 %                                                                             %
874 %                                                                             %
875 %   G e t I m a g e C h a n n e l K u r t o s i s                             %
876 %                                                                             %
877 %                                                                             %
878 %                                                                             %
879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880 %
881 %  GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
882 %  image channels.
883 %
884 %  The format of the GetImageChannelKurtosis method is:
885 %
886 %      MagickBooleanType GetImageChannelKurtosis(const Image *image,
887 %        const ChannelType channel,double *kurtosis,double *skewness,
888 %        ExceptionInfo *exception)
889 %
890 %  A description of each parameter follows:
891 %
892 %    o image: the image.
893 %
894 %    o channel: the channel.
895 %
896 %    o kurtosis: the kurtosis of the channel.
897 %
898 %    o skewness: the skewness of the channel.
899 %
900 %    o exception: return any errors or warnings in this structure.
901 %
902 */
903
904 MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
905   double *kurtosis,double *skewness,ExceptionInfo *exception)
906 {
907   MagickBooleanType
908     status;
909
910   status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
911     exception);
912   return(status);
913 }
914
915 MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
916   const ChannelType channel,double *kurtosis,double *skewness,
917   ExceptionInfo *exception)
918 {
919   double
920     area,
921     mean,
922     standard_deviation,
923     sum_squares,
924     sum_cubes,
925     sum_fourth_power;
926
927   long
928     y;
929
930   assert(image != (Image *) NULL);
931   assert(image->signature == MagickSignature);
932   if (image->debug != MagickFalse)
933     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
934   *kurtosis=0.0;
935   *skewness=0.0;
936   area=0.0;
937   mean=0.0;
938   standard_deviation=0.0;
939   sum_squares=0.0;
940   sum_cubes=0.0;
941   sum_fourth_power=0.0;
942   for (y=0; y < (long) image->rows; y++)
943   {
944     register const IndexPacket
945       *__restrict indexes;
946
947     register const PixelPacket
948       *__restrict p;
949
950     register long
951       x;
952
953     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
954     if (p == (const PixelPacket *) NULL)
955       break;
956     indexes=GetVirtualIndexQueue(image);
957     for (x=0; x < (long) image->columns; x++)
958     {
959       if ((channel & RedChannel) != 0)
960         {
961           mean+=p->red;
962           sum_squares+=(double) p->red*p->red;
963           sum_cubes+=(double) p->red*p->red*p->red;
964           sum_fourth_power+=(double) p->red*p->red*p->red*p->red;
965           area++;
966         }
967       if ((channel & GreenChannel) != 0)
968         {
969           mean+=p->green;
970           sum_squares+=(double) p->green*p->green;
971           sum_cubes+=(double) p->green*p->green*p->green;
972           sum_fourth_power+=(double) p->green*p->green*p->green*p->green;
973           area++;
974         }
975       if ((channel & BlueChannel) != 0)
976         {
977           mean+=p->blue;
978           sum_squares+=(double) p->blue*p->blue;
979           sum_cubes+=(double) p->blue*p->blue*p->blue;
980           sum_fourth_power+=(double) p->blue*p->blue*p->blue*p->blue;
981           area++;
982         }
983       if ((channel & OpacityChannel) != 0)
984         {
985           mean+=p->opacity;
986           sum_squares+=(double) p->opacity*p->opacity;
987           sum_cubes+=(double) p->opacity*p->opacity*p->opacity;
988           sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
989             p->opacity;
990           area++;
991         }
992       if (((channel & IndexChannel) != 0) &&
993           (image->colorspace == CMYKColorspace))
994         {
995           mean+=indexes[x];
996           sum_squares+=(double) indexes[x]*indexes[x];
997           sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
998           sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
999             indexes[x];
1000           area++;
1001         }
1002       p++;
1003     }
1004   }
1005   if (y < (long) image->rows)
1006     return(MagickFalse);
1007   if (area != 0.0)
1008     {
1009       mean/=area;
1010       sum_squares/=area;
1011       sum_cubes/=area;
1012       sum_fourth_power/=area;
1013     }
1014   standard_deviation=sqrt(sum_squares-(mean*mean));
1015   if (standard_deviation != 0.0)
1016     {
1017       *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1018         3.0*mean*mean*mean*mean;
1019       *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1020         standard_deviation;
1021       *kurtosis-=3.0;
1022       *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1023       *skewness/=standard_deviation*standard_deviation*standard_deviation;
1024     }
1025   return(y == (long) image->rows ? MagickTrue : MagickFalse);
1026 }
1027
1028 /*
1029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030 %                                                                             %
1031 %                                                                             %
1032 %                                                                             %
1033 %   G e t I m a g e C h a n n e l R a n g e                                   %
1034 %                                                                             %
1035 %                                                                             %
1036 %                                                                             %
1037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038 %
1039 %  GetImageChannelRange() returns the range of one or more image channels.
1040 %
1041 %  The format of the GetImageChannelRange method is:
1042 %
1043 %      MagickBooleanType GetImageChannelRange(const Image *image,
1044 %        const ChannelType channel,double *minima,double *maxima,
1045 %        ExceptionInfo *exception)
1046 %
1047 %  A description of each parameter follows:
1048 %
1049 %    o image: the image.
1050 %
1051 %    o channel: the channel.
1052 %
1053 %    o minima: the minimum value in the channel.
1054 %
1055 %    o maxima: the maximum value in the channel.
1056 %
1057 %    o exception: return any errors or warnings in this structure.
1058 %
1059 */
1060
1061 MagickExport MagickBooleanType GetImageRange(const Image *image,
1062   double *minima,double *maxima,ExceptionInfo *exception)
1063 {
1064   return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1065 }
1066
1067 MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1068   const ChannelType channel,double *minima,double *maxima,
1069   ExceptionInfo *exception)
1070 {
1071   long
1072     y;
1073
1074   MagickPixelPacket
1075     pixel;
1076
1077   assert(image != (Image *) NULL);
1078   assert(image->signature == MagickSignature);
1079   if (image->debug != MagickFalse)
1080     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1081   *maxima=(-1.0E-37);
1082   *minima=1.0E+37;
1083   GetMagickPixelPacket(image,&pixel);
1084   for (y=0; y < (long) image->rows; y++)
1085   {
1086     register const IndexPacket
1087       *__restrict indexes;
1088
1089     register const PixelPacket
1090       *__restrict p;
1091
1092     register long
1093       x;
1094
1095     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1096     if (p == (const PixelPacket *) NULL)
1097       break;
1098     indexes=GetVirtualIndexQueue(image);
1099     for (x=0; x < (long) image->columns; x++)
1100     {
1101       SetMagickPixelPacket(image,p,indexes+x,&pixel);
1102       if ((channel & RedChannel) != 0)
1103         {
1104           if (pixel.red < *minima)
1105             *minima=(double) pixel.red;
1106           if (pixel.red > *maxima)
1107             *maxima=(double) pixel.red;
1108         }
1109       if ((channel & GreenChannel) != 0)
1110         {
1111           if (pixel.green < *minima)
1112             *minima=(double) pixel.green;
1113           if (pixel.green > *maxima)
1114             *maxima=(double) pixel.green;
1115         }
1116       if ((channel & BlueChannel) != 0)
1117         {
1118           if (pixel.blue < *minima)
1119             *minima=(double) pixel.blue;
1120           if (pixel.blue > *maxima)
1121             *maxima=(double) pixel.blue;
1122         }
1123       if ((channel & OpacityChannel) != 0)
1124         {
1125           if (pixel.opacity < *minima)
1126             *minima=(double) pixel.opacity;
1127           if (pixel.opacity > *maxima)
1128             *maxima=(double) pixel.opacity;
1129         }
1130       if (((channel & IndexChannel) != 0) &&
1131           (image->colorspace == CMYKColorspace))
1132         {
1133           if ((double) indexes[x] < *minima)
1134             *minima=(double) indexes[x];
1135           if ((double) indexes[x] > *maxima)
1136             *maxima=(double) indexes[x];
1137         }
1138       p++;
1139     }
1140   }
1141   return(y == (long) image->rows ? MagickTrue : MagickFalse);
1142 }
1143 \f
1144 /*
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 %                                                                             %
1147 %                                                                             %
1148 %                                                                             %
1149 %   G e t I m a g e C h a n n e l S t a t i s t i c s                         %
1150 %                                                                             %
1151 %                                                                             %
1152 %                                                                             %
1153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154 %
1155 %  GetImageChannelStatistics() returns statistics for each channel in the
1156 %  image.  The statistics include the channel depth, its minima, maxima, mean,
1157 %  standard deviation, kurtosis and skewness.  You can access the red channel
1158 %  mean, for example, like this:
1159 %
1160 %      channel_statistics=GetImageChannelStatistics(image,excepton);
1161 %      red_mean=channel_statistics[RedChannel].mean;
1162 %
1163 %  Use MagickRelinquishMemory() to free the statistics buffer.
1164 %
1165 %  The format of the GetImageChannelStatistics method is:
1166 %
1167 %      ChannelStatistics *GetImageChannelStatistics(const Image *image,
1168 %        ExceptionInfo *exception)
1169 %
1170 %  A description of each parameter follows:
1171 %
1172 %    o image: the image.
1173 %
1174 %    o exception: return any errors or warnings in this structure.
1175 %
1176 */
1177
1178 static inline double MagickMax(const double x,const double y)
1179 {
1180   if (x > y)
1181     return(x);
1182   return(y);
1183 }
1184
1185 static inline double MagickMin(const double x,const double y)
1186 {
1187   if (x < y)
1188     return(x);
1189   return(y);
1190 }
1191
1192 MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1193   ExceptionInfo *exception)
1194 {
1195   ChannelStatistics
1196     *channel_statistics;
1197
1198   double
1199     area,
1200     sum_squares,
1201     sum_cubes;
1202
1203   long
1204     y;
1205
1206   MagickStatusType
1207     status;
1208
1209   QuantumAny
1210     range;
1211
1212   register long
1213     i;
1214
1215   size_t
1216     length;
1217
1218   unsigned long
1219     channels,
1220     depth;
1221
1222   assert(image != (Image *) NULL);
1223   assert(image->signature == MagickSignature);
1224   if (image->debug != MagickFalse)
1225     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1226   length=AllChannels+1UL;
1227   channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1228     sizeof(*channel_statistics));
1229   if (channel_statistics == (ChannelStatistics *) NULL)
1230     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1231   (void) ResetMagickMemory(channel_statistics,0,length*
1232     sizeof(*channel_statistics));
1233   for (i=0; i <= AllChannels; i++)
1234   {
1235     channel_statistics[i].depth=1;
1236     channel_statistics[i].maxima=(-1.0E-37);
1237     channel_statistics[i].minima=1.0E+37;
1238     channel_statistics[i].mean=0.0;
1239     channel_statistics[i].standard_deviation=0.0;
1240     channel_statistics[i].kurtosis=0.0;
1241     channel_statistics[i].skewness=0.0;
1242   }
1243   for (y=0; y < (long) image->rows; y++)
1244   {
1245     register const IndexPacket
1246       *__restrict indexes;
1247
1248     register const PixelPacket
1249       *__restrict p;
1250
1251     register long
1252       x;
1253
1254     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1255     if (p == (const PixelPacket *) NULL)
1256       break;
1257     indexes=GetVirtualIndexQueue(image);
1258     for (x=0; x < (long) image->columns; )
1259     {
1260       if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1261         {
1262           depth=channel_statistics[RedChannel].depth;
1263           range=GetQuantumRange(depth);
1264           status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
1265             range) ? MagickTrue : MagickFalse;
1266           if (status != MagickFalse)
1267             {
1268               channel_statistics[RedChannel].depth++;
1269               continue;
1270             }
1271         }
1272       if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1273         {
1274           depth=channel_statistics[GreenChannel].depth;
1275           range=GetQuantumRange(depth);
1276           status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
1277             range),range) ? MagickTrue : MagickFalse;
1278           if (status != MagickFalse)
1279             {
1280               channel_statistics[GreenChannel].depth++;
1281               continue;
1282             }
1283         }
1284       if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1285         {
1286           depth=channel_statistics[BlueChannel].depth;
1287           range=GetQuantumRange(depth);
1288           status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
1289             range),range) ? MagickTrue : MagickFalse;
1290           if (status != MagickFalse)
1291             {
1292               channel_statistics[BlueChannel].depth++;
1293               continue;
1294             }
1295         }
1296       if (image->matte != MagickFalse)
1297         {
1298           if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1299             {
1300               depth=channel_statistics[OpacityChannel].depth;
1301               range=GetQuantumRange(depth);
1302               status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
1303                 p->opacity,range),range) ? MagickTrue : MagickFalse;
1304               if (status != MagickFalse)
1305                 {
1306                   channel_statistics[OpacityChannel].depth++;
1307                   continue;
1308                 }
1309             }
1310           }
1311       if (image->colorspace == CMYKColorspace)
1312         {
1313           if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1314             {
1315               depth=channel_statistics[BlackChannel].depth;
1316               range=GetQuantumRange(depth);
1317               status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1318                 indexes[x],range),range) ? MagickTrue : MagickFalse;
1319               if (status != MagickFalse)
1320                 {
1321                   channel_statistics[BlackChannel].depth++;
1322                   continue;
1323                 }
1324             }
1325         }
1326       if ((double) p->red < channel_statistics[RedChannel].minima)
1327         channel_statistics[RedChannel].minima=(double) p->red;
1328       if ((double) p->red > channel_statistics[RedChannel].maxima)
1329         channel_statistics[RedChannel].maxima=(double) p->red;
1330       channel_statistics[RedChannel].mean+=p->red;
1331       channel_statistics[RedChannel].standard_deviation+=(double) p->red*p->red;
1332       channel_statistics[RedChannel].kurtosis+=(double) p->red*p->red*
1333         p->red*p->red;
1334       channel_statistics[RedChannel].skewness+=(double) p->red*p->red*p->red;
1335       if ((double) p->green < channel_statistics[GreenChannel].minima)
1336         channel_statistics[GreenChannel].minima=(double) p->green;
1337       if ((double) p->green > channel_statistics[GreenChannel].maxima)
1338         channel_statistics[GreenChannel].maxima=(double) p->green;
1339       channel_statistics[GreenChannel].mean+=p->green;
1340       channel_statistics[GreenChannel].standard_deviation+=(double) p->green*
1341         p->green;
1342       channel_statistics[GreenChannel].kurtosis+=(double) p->green*p->green*
1343         p->green*p->green;
1344       channel_statistics[GreenChannel].skewness+=(double) p->green*p->green*
1345         p->green;
1346       if ((double) p->blue < channel_statistics[BlueChannel].minima)
1347         channel_statistics[BlueChannel].minima=(double) p->blue;
1348       if ((double) p->blue > channel_statistics[BlueChannel].maxima)
1349         channel_statistics[BlueChannel].maxima=(double) p->blue;
1350       channel_statistics[BlueChannel].mean+=p->blue;
1351       channel_statistics[BlueChannel].standard_deviation+=(double) p->blue*
1352         p->blue;
1353       channel_statistics[BlueChannel].kurtosis+=(double) p->blue*p->blue*
1354         p->blue*p->blue;
1355       channel_statistics[BlueChannel].skewness+=(double) p->blue*p->blue*
1356         p->blue;
1357       if (image->matte != MagickFalse)
1358         {
1359           if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
1360             channel_statistics[OpacityChannel].minima=(double) p->opacity;
1361           if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
1362             channel_statistics[OpacityChannel].maxima=(double) p->opacity;
1363           channel_statistics[OpacityChannel].mean+=p->opacity;
1364           channel_statistics[OpacityChannel].standard_deviation+=(double)
1365             p->opacity*p->opacity;
1366           channel_statistics[OpacityChannel].kurtosis+=(double) p->opacity*
1367             p->opacity*p->opacity*p->opacity;
1368           channel_statistics[OpacityChannel].skewness+=(double) p->opacity*
1369             p->opacity*p->opacity;
1370         }
1371       if (image->colorspace == CMYKColorspace)
1372         {
1373           if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1374             channel_statistics[BlackChannel].minima=(double) indexes[x];
1375           if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1376             channel_statistics[BlackChannel].maxima=(double) indexes[x];
1377           channel_statistics[BlackChannel].mean+=indexes[x];
1378           channel_statistics[BlackChannel].standard_deviation+=(double)
1379             indexes[x]*indexes[x];
1380           channel_statistics[BlackChannel].kurtosis+=(double) indexes[x]*
1381             indexes[x]*indexes[x]*indexes[x];
1382           channel_statistics[BlackChannel].skewness+=(double) indexes[x]*
1383             indexes[x]*indexes[x];
1384         }
1385       x++;
1386       p++;
1387     }
1388   }
1389   area=(double) image->columns*image->rows;
1390   for (i=0; i < AllChannels; i++)
1391   {
1392     channel_statistics[i].mean/=area;
1393     channel_statistics[i].standard_deviation/=area;
1394     channel_statistics[i].kurtosis/=area;
1395     channel_statistics[i].skewness/=area;
1396   }
1397   for (i=0; i < AllChannels; i++)
1398   {
1399     channel_statistics[AllChannels].depth=(unsigned long) MagickMax((double)
1400       channel_statistics[AllChannels].depth,(double)
1401       channel_statistics[i].depth);
1402     channel_statistics[AllChannels].minima=MagickMin(
1403       channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1404     channel_statistics[AllChannels].maxima=MagickMax(
1405       channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
1406     channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
1407     channel_statistics[AllChannels].standard_deviation+=
1408       channel_statistics[i].standard_deviation;
1409     channel_statistics[AllChannels].kurtosis+=channel_statistics[i].kurtosis;
1410     channel_statistics[AllChannels].skewness+=channel_statistics[i].skewness;
1411   }
1412   channels=4;
1413   if (image->colorspace == CMYKColorspace)
1414     channels++;
1415   channel_statistics[AllChannels].mean/=channels;
1416   channel_statistics[AllChannels].standard_deviation/=channels;
1417   channel_statistics[AllChannels].kurtosis/=channels;
1418   channel_statistics[AllChannels].skewness/=channels;
1419   for (i=0; i <= AllChannels; i++)
1420   {
1421     sum_squares=0.0;
1422     sum_squares=channel_statistics[i].standard_deviation;
1423     sum_cubes=0.0;
1424     sum_cubes=channel_statistics[i].skewness;
1425     channel_statistics[i].standard_deviation=sqrt(
1426       channel_statistics[i].standard_deviation-
1427        (channel_statistics[i].mean*channel_statistics[i].mean));
1428     if (channel_statistics[i].standard_deviation == 0.0)
1429       {
1430         channel_statistics[i].kurtosis=0.0;
1431         channel_statistics[i].skewness=0.0;
1432       }
1433     else
1434       {
1435         channel_statistics[i].skewness=(channel_statistics[i].skewness-
1436           3.0*channel_statistics[i].mean*sum_squares+
1437           2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1438           channel_statistics[i].mean)/
1439           (channel_statistics[i].standard_deviation*
1440            channel_statistics[i].standard_deviation*
1441            channel_statistics[i].standard_deviation);
1442         channel_statistics[i].kurtosis=(channel_statistics[i].kurtosis-
1443           4.0*channel_statistics[i].mean*sum_cubes+
1444           6.0*channel_statistics[i].mean*channel_statistics[i].mean*sum_squares-
1445           3.0*channel_statistics[i].mean*channel_statistics[i].mean*
1446           1.0*channel_statistics[i].mean*channel_statistics[i].mean)/
1447           (channel_statistics[i].standard_deviation*
1448            channel_statistics[i].standard_deviation*
1449            channel_statistics[i].standard_deviation*
1450            channel_statistics[i].standard_deviation)-3.0;
1451       }
1452   }
1453   return(channel_statistics);
1454 }
1455 \f
1456 /*
1457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458 %                                                                             %
1459 %                                                                             %
1460 %                                                                             %
1461 %   G e t I m a g e Q u a n t u m D e p t h                                   %
1462 %                                                                             %
1463 %                                                                             %
1464 %                                                                             %
1465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466 %
1467 %  GetImageQuantumDepth() returns the depth of the image rounded to a legal
1468 %  quantum depth: 8, 16, or 32.
1469 %
1470 %  The format of the GetImageQuantumDepth method is:
1471 %
1472 %      unsigned long GetImageQuantumDepth(const Image *image,
1473 %        const MagickBooleanType constrain)
1474 %
1475 %  A description of each parameter follows:
1476 %
1477 %    o image: the image.
1478 %
1479 %    o constrain: A value other than MagickFalse, constrains the depth to
1480 %      a maximum of MAGICKCORE_QUANTUM_DEPTH.
1481 %
1482 */
1483 MagickExport unsigned long GetImageQuantumDepth(const Image *image,
1484   const MagickBooleanType constrain)
1485 {
1486   unsigned long
1487     depth;
1488
1489   depth=image->depth;
1490   if (depth <= 8)
1491     depth=8;
1492   else
1493     if (depth <= 16)
1494       depth=16;
1495     else
1496       if (depth <= 32)
1497         depth=32;
1498       else
1499         if (depth <= 64)
1500           depth=64;
1501   if (constrain != MagickFalse)
1502     depth=(unsigned long) MagickMin((double) depth,(double)
1503       MAGICKCORE_QUANTUM_DEPTH);
1504   return(depth);
1505 }
1506 \f
1507 /*
1508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1509 %                                                                             %
1510 %                                                                             %
1511 %                                                                             %
1512 %   S e t I m a g e C h a n n e l D e p t h                                   %
1513 %                                                                             %
1514 %                                                                             %
1515 %                                                                             %
1516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517 %
1518 %  SetImageChannelDepth() sets the depth of the image.
1519 %
1520 %  The format of the SetImageChannelDepth method is:
1521 %
1522 %      MagickBooleanType SetImageDepth(Image *image,const unsigned long depth)
1523 %      MagickBooleanType SetImageChannelDepth(Image *image,
1524 %        const ChannelType channel,const unsigned long depth)
1525 %
1526 %  A description of each parameter follows:
1527 %
1528 %    o image: the image.
1529 %
1530 %    o channel: the channel.
1531 %
1532 %    o depth: the image depth.
1533 %
1534 */
1535
1536 MagickExport MagickBooleanType SetImageDepth(Image *image,
1537   const unsigned long depth)
1538 {
1539   return(SetImageChannelDepth(image,AllChannels,depth));
1540 }
1541
1542 MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
1543   const ChannelType channel,const unsigned long depth)
1544 {
1545   ExceptionInfo
1546     *exception;
1547
1548   long
1549     y;
1550
1551   MagickBooleanType
1552     status;
1553
1554   QuantumAny
1555     range;
1556
1557   CacheView
1558     *image_view;
1559
1560   assert(image != (Image *) NULL);
1561   if (image->debug != MagickFalse)
1562     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1563   assert(image->signature == MagickSignature);
1564   if (GetImageDepth(image,&image->exception) <= (unsigned long)
1565       MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
1566     {
1567       image->depth=depth;
1568       return(MagickTrue);
1569     }
1570   /*
1571     Scale pixels to desired depth.
1572   */
1573   status=MagickTrue;
1574   range=GetQuantumRange(depth);
1575   exception=(&image->exception);
1576   image_view=AcquireCacheView(image);
1577 #if defined(_OPENMP) && (_OPENMP >= 200203)
1578   #pragma omp parallel for schedule(static,1) shared(status)
1579 #endif
1580   for (y=0; y < (long) image->rows; y++)
1581   {
1582     register IndexPacket
1583       *__restrict indexes;
1584
1585     register long
1586       x;
1587
1588     register PixelPacket
1589       *__restrict q;
1590
1591     if (status == MagickFalse)
1592       continue;
1593     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1594       exception);
1595     if (q == (PixelPacket *) NULL)
1596       {
1597         status=MagickFalse;
1598         continue;
1599       }
1600     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1601     for (x=0; x < (long) image->columns; x++)
1602     {
1603       if ((channel & RedChannel) != 0)
1604         q->red=ScaleAnyToQuantum(ScaleQuantumToAny(q->red,range),range);
1605       if ((channel & GreenChannel) != 0)
1606         q->green=ScaleAnyToQuantum(ScaleQuantumToAny(q->green,range),range);
1607       if ((channel & BlueChannel) != 0)
1608         q->blue=ScaleAnyToQuantum(ScaleQuantumToAny(q->blue,range),range);
1609       if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
1610         q->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(q->opacity,range),range);
1611       if (((channel & IndexChannel) != 0) &&
1612           (image->colorspace == CMYKColorspace))
1613         indexes[x]=ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],range),range);
1614       q++;
1615     }
1616     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1617       {
1618         status=MagickFalse;
1619         continue;
1620       }
1621   }
1622   image_view=DestroyCacheView(image_view);
1623   if (image->storage_class == PseudoClass)
1624     {
1625       QuantumAny
1626         range;
1627
1628       register long
1629         i;
1630
1631       register PixelPacket
1632         *__restrict p;
1633
1634       p=image->colormap;
1635       range=GetQuantumRange(depth);
1636 #if defined(_OPENMP) && (_OPENMP >= 200203)
1637   #pragma omp parallel for schedule(static,1) shared(status)
1638 #endif
1639       for (i=0; i < (long) image->colors; i++)
1640       {
1641         if ((channel & RedChannel) != 0)
1642           p->red=ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),range);
1643         if ((channel & GreenChannel) != 0)
1644           p->green=ScaleAnyToQuantum(ScaleQuantumToAny(p->green,range),range);
1645         if ((channel & BlueChannel) != 0)
1646           p->blue=ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),range);
1647         if ((channel & OpacityChannel) != 0)
1648           p->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,range),
1649             range);
1650         p++;
1651       }
1652     }
1653   image->depth=depth;
1654   return(status);
1655 }