2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Methods %
20 % Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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/monitor-private.h"
76 #include "magick/option.h"
77 #include "magick/paint.h"
78 #include "magick/pixel-private.h"
79 #include "magick/profile.h"
80 #include "magick/quantize.h"
81 #include "magick/random_.h"
82 #include "magick/segment.h"
83 #include "magick/semaphore.h"
84 #include "magick/signature-private.h"
85 #include "magick/statistic.h"
86 #include "magick/string_.h"
87 #include "magick/thread-private.h"
88 #include "magick/timer.h"
89 #include "magick/utility.h"
90 #include "magick/version.h"
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % A v e r a g e I m a g e s %
101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103 % AverageImages() takes a set of images and averages them together. Each
104 % image in the set must have the same width and height. AverageImages()
105 % returns a single image with each corresponding pixel component of each
106 % image averaged. On failure, a NULL image is returned and exception
107 % describes the reason for the failure.
109 % The format of the AverageImages method is:
111 % Image *AverageImages(Image *image,ExceptionInfo *exception)
113 % A description of each parameter follows:
115 % o image: the image sequence.
117 % o exception: return any errors or warnings in this structure.
121 static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
126 assert(pixels != (MagickPixelPacket **) NULL);
127 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
128 if (pixels[i] != (MagickPixelPacket *) NULL)
129 pixels[i]=(MagickPixelPacket *) RelinquishMagickMemory(pixels[i]);
130 pixels=(MagickPixelPacket **) RelinquishAlignedMemory(pixels);
134 static MagickPixelPacket **AcquirePixelThreadSet(const Image *image)
146 number_threads=GetOpenMPMaximumThreads();
147 pixels=(MagickPixelPacket **) AcquireAlignedMemory(number_threads,
149 if (pixels == (MagickPixelPacket **) NULL)
150 return((MagickPixelPacket **) NULL);
151 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
152 for (i=0; i < (long) number_threads; i++)
154 pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(image->columns,
156 if (pixels[i] == (MagickPixelPacket *) NULL)
157 return(DestroyPixelThreadSet(pixels));
158 for (j=0; j < (long) image->columns; j++)
159 GetMagickPixelPacket(image,&pixels[i][j]);
164 MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
166 #define AverageImageTag "Average/Image"
192 Ensure the image are the same size.
194 assert(image != (Image *) NULL);
195 assert(image->signature == MagickSignature);
196 if (image->debug != MagickFalse)
197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
198 assert(exception != (ExceptionInfo *) NULL);
199 assert(exception->signature == MagickSignature);
200 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
201 if ((next->columns != image->columns) || (next->rows != image->rows))
202 ThrowImageException(OptionError,"ImageWidthsOrHeightsDiffer");
204 Initialize average next attributes.
206 average_image=CloneImage(image,image->columns,image->rows,MagickTrue,
208 if (average_image == (Image *) NULL)
209 return((Image *) NULL);
210 if (SetImageStorageClass(average_image,DirectClass) == MagickFalse)
212 InheritException(exception,&average_image->exception);
213 average_image=DestroyImage(average_image);
214 return((Image *) NULL);
216 average_pixels=AcquirePixelThreadSet(image);
217 if (average_pixels == (MagickPixelPacket **) NULL)
219 average_image=DestroyImage(average_image);
220 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
223 Average image pixels.
227 GetMagickPixelPacket(image,&zero);
228 number_images=GetImageListLength(image);
229 average_view=AcquireCacheView(average_image);
230 #if defined(_OPENMP) && (_OPENMP >= 200203)
231 #pragma omp parallel for schedule(static,1) shared(progress,status)
233 for (y=0; y < (long) average_image->rows; y++)
245 *__restrict average_indexes;
252 register MagickPixelPacket
258 if (status == MagickFalse)
260 q=QueueCacheViewAuthenticPixels(average_view,0,y,average_image->columns,1,
262 if (q == (PixelPacket *) NULL)
267 average_indexes=GetCacheViewAuthenticIndexQueue(average_view);
269 id=GetOpenMPThreadId();
270 average_pixel=average_pixels[id];
271 for (x=0; x < (long) average_image->columns; x++)
272 average_pixel[x]=zero;
274 for (i=0; i < (long) number_images; i++)
276 register const IndexPacket
279 register const PixelPacket
282 image_view=AcquireCacheView(next);
283 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
284 if (p == (const PixelPacket *) NULL)
286 image_view=DestroyCacheView(image_view);
289 indexes=GetCacheViewVirtualIndexQueue(image_view);
290 for (x=0; x < (long) next->columns; x++)
292 SetMagickPixelPacket(next,p,indexes+x,&pixel);
293 average_pixel[x].red+=QuantumScale*pixel.red;
294 average_pixel[x].green+=QuantumScale*pixel.green;
295 average_pixel[x].blue+=QuantumScale*pixel.blue;
296 average_pixel[x].opacity+=QuantumScale*pixel.opacity;
297 if (average_image->colorspace == CMYKColorspace)
298 average_pixel[x].index+=QuantumScale*pixel.index;
301 image_view=DestroyCacheView(image_view);
302 next=GetNextImageInList(next);
304 for (x=0; x < (long) average_image->columns; x++)
306 average_pixel[x].red=(MagickRealType) (QuantumRange*
307 average_pixel[x].red/number_images);
308 average_pixel[x].green=(MagickRealType) (QuantumRange*
309 average_pixel[x].green/number_images);
310 average_pixel[x].blue=(MagickRealType) (QuantumRange*
311 average_pixel[x].blue/number_images);
312 average_pixel[x].opacity=(MagickRealType) (QuantumRange*
313 average_pixel[x].opacity/number_images);
314 if (average_image->colorspace == CMYKColorspace)
315 average_pixel[x].index=(MagickRealType) (QuantumRange*
316 average_pixel[x].index/number_images);
317 SetPixelPacket(average_image,&average_pixel[x],q,average_indexes+x);
320 if (SyncCacheViewAuthenticPixels(average_view,exception) == MagickFalse)
322 if (image->progress_monitor != (MagickProgressMonitor) NULL)
327 #if defined(_OPENMP) && (_OPENMP >= 200203)
328 #pragma omp critical (MagickCore_AverageImages)
330 proceed=SetImageProgress(image,AverageImageTag,progress++,
331 average_image->rows);
332 if (proceed == MagickFalse)
336 average_view=DestroyCacheView(average_view);
337 average_pixels=DestroyPixelThreadSet(average_pixels);
338 if (status == MagickFalse)
339 average_image=DestroyImage(average_image);
340 return(average_image);
344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348 + G e t I m a g e B o u n d i n g B o x %
352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
354 % GetImageBoundingBox() returns the bounding box of an image canvas.
356 % The format of the GetImageBoundingBox method is:
358 % RectangleInfo GetImageBoundingBox(const Image *image,
359 % ExceptionInfo *exception)
361 % A description of each parameter follows:
363 % o bounds: Method GetImageBoundingBox returns the bounding box of an
366 % o image: the image.
368 % o exception: return any errors or warnings in this structure.
371 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
372 ExceptionInfo *exception)
387 register const PixelPacket
393 assert(image != (Image *) NULL);
394 assert(image->signature == MagickSignature);
395 if (image->debug != MagickFalse)
396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
399 bounds.x=(long) image->columns;
400 bounds.y=(long) image->rows;
401 GetMagickPixelPacket(image,&target[0]);
402 image_view=AcquireCacheView(image);
403 p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
404 if (p == (const PixelPacket *) NULL)
406 image_view=DestroyCacheView(image_view);
409 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
411 GetMagickPixelPacket(image,&target[1]);
412 p=GetCacheViewVirtualPixels(image_view,(long) image->columns-1,0,1,1,
414 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
416 GetMagickPixelPacket(image,&target[2]);
417 p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-1,1,1,exception);
418 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
421 GetMagickPixelPacket(image,&zero);
422 #if defined(_OPENMP) && (_OPENMP >= 200203)
423 #pragma omp parallel for schedule(static,1) shared(status)
425 for (y=0; y < (long) image->rows; y++)
433 register const IndexPacket
436 register const PixelPacket
442 if (status == MagickFalse)
444 #if defined(HAVE_OPENMP)
445 # pragma omp critical (MagickCore_GetImageBoundingBox)
448 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
449 if (p == (const PixelPacket *) NULL)
454 indexes=GetCacheViewVirtualIndexQueue(image_view);
456 for (x=0; x < (long) image->columns; x++)
458 SetMagickPixelPacket(image,p,indexes+x,&pixel);
459 if ((x < bounding_box.x) &&
460 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
462 if ((x > (long) bounding_box.width) &&
463 (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
464 bounding_box.width=(unsigned long) x;
465 if ((y < bounding_box.y) &&
466 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
468 if ((y > (long) bounding_box.height) &&
469 (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
470 bounding_box.height=(unsigned long) y;
473 #if defined(HAVE_OPENMP)
474 # pragma omp critical (MagickCore_GetImageBoundingBox)
477 if (bounding_box.x < bounds.x)
478 bounds.x=bounding_box.x;
479 if (bounding_box.y < bounds.y)
480 bounds.y=bounding_box.y;
481 if (bounding_box.width > bounds.width)
482 bounds.width=bounding_box.width;
483 if (bounding_box.height > bounds.height)
484 bounds.height=bounding_box.height;
487 image_view=DestroyCacheView(image_view);
488 if ((bounds.width == 0) || (bounds.height == 0))
489 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
490 "GeometryDoesNotContainImage","`%s'",image->filename);
493 bounds.width-=(bounds.x-1);
494 bounds.height-=(bounds.y-1);
500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 % G e t I m a g e C h a n n e l D e p t h %
508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 % GetImageChannelDepth() returns the depth of a particular image channel.
512 % The format of the GetImageChannelDepth method is:
514 % unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception)
515 % unsigned long GetImageChannelDepth(const Image *image,
516 % const ChannelType channel,ExceptionInfo *exception)
518 % A description of each parameter follows:
520 % o image: the image.
522 % o channel: the channel.
524 % o exception: return any errors or warnings in this structure.
528 MagickExport unsigned long GetImageDepth(const Image *image,
529 ExceptionInfo *exception)
531 return(GetImageChannelDepth(image,AllChannels,exception));
534 MagickExport unsigned long GetImageChannelDepth(const Image *image,
535 const ChannelType channel,ExceptionInfo *exception)
557 assert(image != (Image *) NULL);
558 assert(image->signature == MagickSignature);
559 if (image->debug != MagickFalse)
560 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
561 number_threads=GetOpenMPMaximumThreads();
562 current_depth=(unsigned long *) AcquireQuantumMemory(number_threads,
563 sizeof(*current_depth));
564 if (current_depth == (unsigned long *) NULL)
565 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
567 for (id=0; id < (long) number_threads; id++)
569 if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
571 register const PixelPacket
578 #if defined(_OPENMP) && (_OPENMP >= 200203)
579 #pragma omp parallel for schedule(static,1) shared(status)
581 for (i=0; i < (long) image->colors; i++)
583 if (status == MagickFalse)
585 id=GetOpenMPThreadId();
586 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
595 range=GetQuantumRange(current_depth[id]);
596 if ((channel & RedChannel) != 0)
597 status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,
599 if ((channel & GreenChannel) != 0)
600 status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
602 if ((channel & BlueChannel) != 0)
603 status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
611 depth=current_depth[0];
612 for (id=1; id < (long) number_threads; id++)
613 if (depth < current_depth[id])
614 depth=current_depth[id];
615 current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
618 image_view=AcquireCacheView(image);
619 #if defined(_OPENMP) && (_OPENMP >= 200203)
620 #pragma omp parallel for schedule(static,1) shared(status)
622 for (y=0; y < (long) image->rows; y++)
624 register const IndexPacket
627 register const PixelPacket
634 if (status == MagickFalse)
636 id=GetOpenMPThreadId();
637 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
638 if (p == (const PixelPacket *) NULL)
640 indexes=GetCacheViewVirtualIndexQueue(image_view);
641 for (x=0; x < (long) image->columns; x++)
643 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
652 range=GetQuantumRange(current_depth[id]);
653 if ((channel & RedChannel) != 0)
654 status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
656 if ((channel & GreenChannel) != 0)
657 status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
659 if ((channel & BlueChannel) != 0)
660 status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),
662 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
663 status|=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,
665 if (((channel & IndexChannel) != 0) &&
666 (image->colorspace == CMYKColorspace))
667 status|=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],
675 if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
678 image_view=DestroyCacheView(image_view);
679 depth=current_depth[0];
680 for (id=1; id < (long) number_threads; id++)
681 if (depth < current_depth[id])
682 depth=current_depth[id];
683 current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692 + G e t I m a g e C h a n n e l E x t r e m a %
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 % GetImageChannelExtrema() returns the extrema of one or more image channels.
700 % The format of the GetImageChannelExtrema method is:
702 % MagickBooleanType GetImageChannelExtrema(const Image *image,
703 % const ChannelType channel,unsigned long *minima,unsigned long *maxima,
704 % ExceptionInfo *exception)
706 % A description of each parameter follows:
708 % o image: the image.
710 % o channel: the channel.
712 % o minima: the minimum value in the channel.
714 % o maxima: the maximum value in the channel.
716 % o exception: return any errors or warnings in this structure.
720 MagickExport MagickBooleanType GetImageExtrema(const Image *image,
721 unsigned long *minima,unsigned long *maxima,ExceptionInfo *exception)
723 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
726 MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
727 const ChannelType channel,unsigned long *minima,unsigned long *maxima,
728 ExceptionInfo *exception)
737 assert(image != (Image *) NULL);
738 assert(image->signature == MagickSignature);
739 if (image->debug != MagickFalse)
740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
741 status=GetImageChannelRange(image,channel,&min,&max,exception);
742 *minima=(unsigned long) (min+0.5);
743 *maxima=(unsigned long) (max+0.5);
748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752 % G e t I m a g e C h a n n e l M e a n %
756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758 % GetImageChannelMean() returns the mean and standard deviation of one or more
761 % The format of the GetImageChannelMean method is:
763 % MagickBooleanType GetImageChannelMean(const Image *image,
764 % const ChannelType channel,double *mean,double *standard_deviation,
765 % ExceptionInfo *exception)
767 % A description of each parameter follows:
769 % o image: the image.
771 % o channel: the channel.
773 % o mean: the average value in the channel.
775 % o standard_deviation: the standard deviation of the channel.
777 % o exception: return any errors or warnings in this structure.
781 MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
782 double *standard_deviation,ExceptionInfo *exception)
787 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
792 MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
793 const ChannelType channel,double *mean,double *standard_deviation,
794 ExceptionInfo *exception)
802 assert(image != (Image *) NULL);
803 assert(image->signature == MagickSignature);
804 if (image->debug != MagickFalse)
805 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
807 *standard_deviation=0.0;
809 for (y=0; y < (long) image->rows; y++)
811 register const IndexPacket
814 register const PixelPacket
820 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
821 if (p == (const PixelPacket *) NULL)
823 indexes=GetVirtualIndexQueue(image);
824 for (x=0; x < (long) image->columns; x++)
826 if ((channel & RedChannel) != 0)
829 *standard_deviation+=(double) p->red*p->red;
832 if ((channel & GreenChannel) != 0)
835 *standard_deviation+=(double) p->green*p->green;
838 if ((channel & BlueChannel) != 0)
841 *standard_deviation+=(double) p->blue*p->blue;
844 if ((channel & OpacityChannel) != 0)
847 *standard_deviation+=(double) p->opacity*p->opacity;
850 if (((channel & IndexChannel) != 0) &&
851 (image->colorspace == CMYKColorspace))
854 *standard_deviation+=(double) indexes[x]*indexes[x];
860 if (y < (long) image->rows)
865 *standard_deviation/=area;
867 *standard_deviation=sqrt(*standard_deviation-(*mean*(*mean)));
868 return(y == (long) image->rows ? MagickTrue : MagickFalse);
872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876 % G e t I m a g e C h a n n e l K u r t o s i s %
880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882 % GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
885 % The format of the GetImageChannelKurtosis method is:
887 % MagickBooleanType GetImageChannelKurtosis(const Image *image,
888 % const ChannelType channel,double *kurtosis,double *skewness,
889 % ExceptionInfo *exception)
891 % A description of each parameter follows:
893 % o image: the image.
895 % o channel: the channel.
897 % o kurtosis: the kurtosis of the channel.
899 % o skewness: the skewness of the channel.
901 % o exception: return any errors or warnings in this structure.
905 MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
906 double *kurtosis,double *skewness,ExceptionInfo *exception)
911 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
916 MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
917 const ChannelType channel,double *kurtosis,double *skewness,
918 ExceptionInfo *exception)
931 assert(image != (Image *) NULL);
932 assert(image->signature == MagickSignature);
933 if (image->debug != MagickFalse)
934 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
939 standard_deviation=0.0;
942 sum_fourth_power=0.0;
943 for (y=0; y < (long) image->rows; y++)
945 register const IndexPacket
948 register const PixelPacket
954 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
955 if (p == (const PixelPacket *) NULL)
957 indexes=GetVirtualIndexQueue(image);
958 for (x=0; x < (long) image->columns; x++)
960 if ((channel & RedChannel) != 0)
963 sum_squares+=(double) p->red*p->red;
964 sum_cubes+=(double) p->red*p->red*p->red;
965 sum_fourth_power+=(double) p->red*p->red*p->red*p->red;
968 if ((channel & GreenChannel) != 0)
971 sum_squares+=(double) p->green*p->green;
972 sum_cubes+=(double) p->green*p->green*p->green;
973 sum_fourth_power+=(double) p->green*p->green*p->green*p->green;
976 if ((channel & BlueChannel) != 0)
979 sum_squares+=(double) p->blue*p->blue;
980 sum_cubes+=(double) p->blue*p->blue*p->blue;
981 sum_fourth_power+=(double) p->blue*p->blue*p->blue*p->blue;
984 if ((channel & OpacityChannel) != 0)
987 sum_squares+=(double) p->opacity*p->opacity;
988 sum_cubes+=(double) p->opacity*p->opacity*p->opacity;
989 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
993 if (((channel & IndexChannel) != 0) &&
994 (image->colorspace == CMYKColorspace))
997 sum_squares+=(double) indexes[x]*indexes[x];
998 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
999 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1006 if (y < (long) image->rows)
1007 return(MagickFalse);
1013 sum_fourth_power/=area;
1015 standard_deviation=sqrt(sum_squares-(mean*mean));
1016 if (standard_deviation != 0.0)
1018 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1019 3.0*mean*mean*mean*mean;
1020 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1023 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1024 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1026 return(y == (long) image->rows ? MagickTrue : MagickFalse);
1030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1034 % G e t I m a g e C h a n n e l R a n g e %
1038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1040 % GetImageChannelRange() returns the range of one or more image channels.
1042 % The format of the GetImageChannelRange method is:
1044 % MagickBooleanType GetImageChannelRange(const Image *image,
1045 % const ChannelType channel,double *minima,double *maxima,
1046 % ExceptionInfo *exception)
1048 % A description of each parameter follows:
1050 % o image: the image.
1052 % o channel: the channel.
1054 % o minima: the minimum value in the channel.
1056 % o maxima: the maximum value in the channel.
1058 % o exception: return any errors or warnings in this structure.
1062 MagickExport MagickBooleanType GetImageRange(const Image *image,
1063 double *minima,double *maxima,ExceptionInfo *exception)
1065 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1068 MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1069 const ChannelType channel,double *minima,double *maxima,
1070 ExceptionInfo *exception)
1078 assert(image != (Image *) NULL);
1079 assert(image->signature == MagickSignature);
1080 if (image->debug != MagickFalse)
1081 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1084 GetMagickPixelPacket(image,&pixel);
1085 for (y=0; y < (long) image->rows; y++)
1087 register const IndexPacket
1088 *__restrict indexes;
1090 register const PixelPacket
1096 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1097 if (p == (const PixelPacket *) NULL)
1099 indexes=GetVirtualIndexQueue(image);
1100 for (x=0; x < (long) image->columns; x++)
1102 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1103 if ((channel & RedChannel) != 0)
1105 if (pixel.red < *minima)
1106 *minima=(double) pixel.red;
1107 if (pixel.red > *maxima)
1108 *maxima=(double) pixel.red;
1110 if ((channel & GreenChannel) != 0)
1112 if (pixel.green < *minima)
1113 *minima=(double) pixel.green;
1114 if (pixel.green > *maxima)
1115 *maxima=(double) pixel.green;
1117 if ((channel & BlueChannel) != 0)
1119 if (pixel.blue < *minima)
1120 *minima=(double) pixel.blue;
1121 if (pixel.blue > *maxima)
1122 *maxima=(double) pixel.blue;
1124 if ((channel & OpacityChannel) != 0)
1126 if (pixel.opacity < *minima)
1127 *minima=(double) pixel.opacity;
1128 if (pixel.opacity > *maxima)
1129 *maxima=(double) pixel.opacity;
1131 if (((channel & IndexChannel) != 0) &&
1132 (image->colorspace == CMYKColorspace))
1134 if ((double) indexes[x] < *minima)
1135 *minima=(double) indexes[x];
1136 if ((double) indexes[x] > *maxima)
1137 *maxima=(double) indexes[x];
1142 return(y == (long) image->rows ? MagickTrue : MagickFalse);
1146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1150 % 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 %
1154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1156 % GetImageChannelStatistics() returns statistics for each channel in the
1157 % image. The statistics include the channel depth, its minima, maxima, mean,
1158 % standard deviation, kurtosis and skewness. You can access the red channel
1159 % mean, for example, like this:
1161 % channel_statistics=GetImageChannelStatistics(image,excepton);
1162 % red_mean=channel_statistics[RedChannel].mean;
1164 % Use MagickRelinquishMemory() to free the statistics buffer.
1166 % The format of the GetImageChannelStatistics method is:
1168 % ChannelStatistics *GetImageChannelStatistics(const Image *image,
1169 % ExceptionInfo *exception)
1171 % A description of each parameter follows:
1173 % o image: the image.
1175 % o exception: return any errors or warnings in this structure.
1179 static inline double MagickMax(const double x,const double y)
1186 static inline double MagickMin(const double x,const double y)
1193 MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1194 ExceptionInfo *exception)
1197 *channel_statistics;
1223 assert(image != (Image *) NULL);
1224 assert(image->signature == MagickSignature);
1225 if (image->debug != MagickFalse)
1226 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1227 length=AllChannels+1UL;
1228 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1229 sizeof(*channel_statistics));
1230 if (channel_statistics == (ChannelStatistics *) NULL)
1231 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1232 (void) ResetMagickMemory(channel_statistics,0,length*
1233 sizeof(*channel_statistics));
1234 for (i=0; i <= AllChannels; i++)
1236 channel_statistics[i].depth=1;
1237 channel_statistics[i].maxima=(-1.0E-37);
1238 channel_statistics[i].minima=1.0E+37;
1239 channel_statistics[i].mean=0.0;
1240 channel_statistics[i].standard_deviation=0.0;
1241 channel_statistics[i].kurtosis=0.0;
1242 channel_statistics[i].skewness=0.0;
1244 for (y=0; y < (long) image->rows; y++)
1246 register const IndexPacket
1247 *__restrict indexes;
1249 register const PixelPacket
1255 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1256 if (p == (const PixelPacket *) NULL)
1258 indexes=GetVirtualIndexQueue(image);
1259 for (x=0; x < (long) image->columns; )
1261 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1263 depth=channel_statistics[RedChannel].depth;
1264 range=GetQuantumRange(depth);
1265 status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
1266 range) ? MagickTrue : MagickFalse;
1267 if (status != MagickFalse)
1269 channel_statistics[RedChannel].depth++;
1273 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1275 depth=channel_statistics[GreenChannel].depth;
1276 range=GetQuantumRange(depth);
1277 status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
1278 range),range) ? MagickTrue : MagickFalse;
1279 if (status != MagickFalse)
1281 channel_statistics[GreenChannel].depth++;
1285 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1287 depth=channel_statistics[BlueChannel].depth;
1288 range=GetQuantumRange(depth);
1289 status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
1290 range),range) ? MagickTrue : MagickFalse;
1291 if (status != MagickFalse)
1293 channel_statistics[BlueChannel].depth++;
1297 if (image->matte != MagickFalse)
1299 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1301 depth=channel_statistics[OpacityChannel].depth;
1302 range=GetQuantumRange(depth);
1303 status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
1304 p->opacity,range),range) ? MagickTrue : MagickFalse;
1305 if (status != MagickFalse)
1307 channel_statistics[OpacityChannel].depth++;
1312 if (image->colorspace == CMYKColorspace)
1314 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1316 depth=channel_statistics[BlackChannel].depth;
1317 range=GetQuantumRange(depth);
1318 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1319 indexes[x],range),range) ? MagickTrue : MagickFalse;
1320 if (status != MagickFalse)
1322 channel_statistics[BlackChannel].depth++;
1327 if ((double) p->red < channel_statistics[RedChannel].minima)
1328 channel_statistics[RedChannel].minima=(double) p->red;
1329 if ((double) p->red > channel_statistics[RedChannel].maxima)
1330 channel_statistics[RedChannel].maxima=(double) p->red;
1331 channel_statistics[RedChannel].mean+=p->red;
1332 channel_statistics[RedChannel].standard_deviation+=(double) p->red*p->red;
1333 channel_statistics[RedChannel].kurtosis+=(double) p->red*p->red*
1335 channel_statistics[RedChannel].skewness+=(double) p->red*p->red*p->red;
1336 if ((double) p->green < channel_statistics[GreenChannel].minima)
1337 channel_statistics[GreenChannel].minima=(double) p->green;
1338 if ((double) p->green > channel_statistics[GreenChannel].maxima)
1339 channel_statistics[GreenChannel].maxima=(double) p->green;
1340 channel_statistics[GreenChannel].mean+=p->green;
1341 channel_statistics[GreenChannel].standard_deviation+=(double) p->green*
1343 channel_statistics[GreenChannel].kurtosis+=(double) p->green*p->green*
1345 channel_statistics[GreenChannel].skewness+=(double) p->green*p->green*
1347 if ((double) p->blue < channel_statistics[BlueChannel].minima)
1348 channel_statistics[BlueChannel].minima=(double) p->blue;
1349 if ((double) p->blue > channel_statistics[BlueChannel].maxima)
1350 channel_statistics[BlueChannel].maxima=(double) p->blue;
1351 channel_statistics[BlueChannel].mean+=p->blue;
1352 channel_statistics[BlueChannel].standard_deviation+=(double) p->blue*
1354 channel_statistics[BlueChannel].kurtosis+=(double) p->blue*p->blue*
1356 channel_statistics[BlueChannel].skewness+=(double) p->blue*p->blue*
1358 if (image->matte != MagickFalse)
1360 if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
1361 channel_statistics[OpacityChannel].minima=(double) p->opacity;
1362 if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
1363 channel_statistics[OpacityChannel].maxima=(double) p->opacity;
1364 channel_statistics[OpacityChannel].mean+=p->opacity;
1365 channel_statistics[OpacityChannel].standard_deviation+=(double)
1366 p->opacity*p->opacity;
1367 channel_statistics[OpacityChannel].kurtosis+=(double) p->opacity*
1368 p->opacity*p->opacity*p->opacity;
1369 channel_statistics[OpacityChannel].skewness+=(double) p->opacity*
1370 p->opacity*p->opacity;
1372 if (image->colorspace == CMYKColorspace)
1374 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1375 channel_statistics[BlackChannel].minima=(double) indexes[x];
1376 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1377 channel_statistics[BlackChannel].maxima=(double) indexes[x];
1378 channel_statistics[BlackChannel].mean+=indexes[x];
1379 channel_statistics[BlackChannel].standard_deviation+=(double)
1380 indexes[x]*indexes[x];
1381 channel_statistics[BlackChannel].kurtosis+=(double) indexes[x]*
1382 indexes[x]*indexes[x]*indexes[x];
1383 channel_statistics[BlackChannel].skewness+=(double) indexes[x]*
1384 indexes[x]*indexes[x];
1390 area=(double) image->columns*image->rows;
1391 for (i=0; i < AllChannels; i++)
1393 channel_statistics[i].mean/=area;
1394 channel_statistics[i].standard_deviation/=area;
1395 channel_statistics[i].kurtosis/=area;
1396 channel_statistics[i].skewness/=area;
1398 for (i=0; i < AllChannels; i++)
1400 channel_statistics[AllChannels].depth=(unsigned long) MagickMax((double)
1401 channel_statistics[AllChannels].depth,(double)
1402 channel_statistics[i].depth);
1403 channel_statistics[AllChannels].minima=MagickMin(
1404 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1405 channel_statistics[AllChannels].maxima=MagickMax(
1406 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
1407 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
1408 channel_statistics[AllChannels].standard_deviation+=
1409 channel_statistics[i].standard_deviation;
1410 channel_statistics[AllChannels].kurtosis+=channel_statistics[i].kurtosis;
1411 channel_statistics[AllChannels].skewness+=channel_statistics[i].skewness;
1414 if (image->colorspace == CMYKColorspace)
1416 channel_statistics[AllChannels].mean/=channels;
1417 channel_statistics[AllChannels].standard_deviation/=channels;
1418 channel_statistics[AllChannels].kurtosis/=channels;
1419 channel_statistics[AllChannels].skewness/=channels;
1420 for (i=0; i <= AllChannels; i++)
1423 sum_squares=channel_statistics[i].standard_deviation;
1425 sum_cubes=channel_statistics[i].skewness;
1426 channel_statistics[i].standard_deviation=sqrt(
1427 channel_statistics[i].standard_deviation-
1428 (channel_statistics[i].mean*channel_statistics[i].mean));
1429 if (channel_statistics[i].standard_deviation == 0.0)
1431 channel_statistics[i].kurtosis=0.0;
1432 channel_statistics[i].skewness=0.0;
1436 channel_statistics[i].skewness=(channel_statistics[i].skewness-
1437 3.0*channel_statistics[i].mean*sum_squares+
1438 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1439 channel_statistics[i].mean)/
1440 (channel_statistics[i].standard_deviation*
1441 channel_statistics[i].standard_deviation*
1442 channel_statistics[i].standard_deviation);
1443 channel_statistics[i].kurtosis=(channel_statistics[i].kurtosis-
1444 4.0*channel_statistics[i].mean*sum_cubes+
1445 6.0*channel_statistics[i].mean*channel_statistics[i].mean*sum_squares-
1446 3.0*channel_statistics[i].mean*channel_statistics[i].mean*
1447 1.0*channel_statistics[i].mean*channel_statistics[i].mean)/
1448 (channel_statistics[i].standard_deviation*
1449 channel_statistics[i].standard_deviation*
1450 channel_statistics[i].standard_deviation*
1451 channel_statistics[i].standard_deviation)-3.0;
1454 return(channel_statistics);
1458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462 % G e t I m a g e Q u a n t u m D e p t h %
1466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468 % GetImageQuantumDepth() returns the depth of the image rounded to a legal
1469 % quantum depth: 8, 16, or 32.
1471 % The format of the GetImageQuantumDepth method is:
1473 % unsigned long GetImageQuantumDepth(const Image *image,
1474 % const MagickBooleanType constrain)
1476 % A description of each parameter follows:
1478 % o image: the image.
1480 % o constrain: A value other than MagickFalse, constrains the depth to
1481 % a maximum of MAGICKCORE_QUANTUM_DEPTH.
1484 MagickExport unsigned long GetImageQuantumDepth(const Image *image,
1485 const MagickBooleanType constrain)
1502 if (constrain != MagickFalse)
1503 depth=(unsigned long) MagickMin((double) depth,(double)
1504 MAGICKCORE_QUANTUM_DEPTH);
1509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513 % S e t I m a g e C h a n n e l D e p t h %
1517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1519 % SetImageChannelDepth() sets the depth of the image.
1521 % The format of the SetImageChannelDepth method is:
1523 % MagickBooleanType SetImageDepth(Image *image,const unsigned long depth)
1524 % MagickBooleanType SetImageChannelDepth(Image *image,
1525 % const ChannelType channel,const unsigned long depth)
1527 % A description of each parameter follows:
1529 % o image: the image.
1531 % o channel: the channel.
1533 % o depth: the image depth.
1537 MagickExport MagickBooleanType SetImageDepth(Image *image,
1538 const unsigned long depth)
1540 return(SetImageChannelDepth(image,AllChannels,depth));
1543 MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
1544 const ChannelType channel,const unsigned long depth)
1561 assert(image != (Image *) NULL);
1562 if (image->debug != MagickFalse)
1563 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1564 assert(image->signature == MagickSignature);
1565 if (GetImageDepth(image,&image->exception) <= (unsigned long)
1566 MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
1572 Scale pixels to desired depth.
1575 range=GetQuantumRange(depth);
1576 exception=(&image->exception);
1577 image_view=AcquireCacheView(image);
1578 #if defined(_OPENMP) && (_OPENMP >= 200203)
1579 #pragma omp parallel for schedule(static,1) shared(status)
1581 for (y=0; y < (long) image->rows; y++)
1583 register IndexPacket
1584 *__restrict indexes;
1589 register PixelPacket
1592 if (status == MagickFalse)
1594 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1596 if (q == (PixelPacket *) NULL)
1601 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1602 for (x=0; x < (long) image->columns; x++)
1604 if ((channel & RedChannel) != 0)
1605 q->red=ScaleAnyToQuantum(ScaleQuantumToAny(q->red,range),range);
1606 if ((channel & GreenChannel) != 0)
1607 q->green=ScaleAnyToQuantum(ScaleQuantumToAny(q->green,range),range);
1608 if ((channel & BlueChannel) != 0)
1609 q->blue=ScaleAnyToQuantum(ScaleQuantumToAny(q->blue,range),range);
1610 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
1611 q->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(q->opacity,range),range);
1612 if (((channel & IndexChannel) != 0) &&
1613 (image->colorspace == CMYKColorspace))
1614 indexes[x]=ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],range),range);
1617 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1623 image_view=DestroyCacheView(image_view);
1624 if (image->storage_class == PseudoClass)
1632 register PixelPacket
1636 range=GetQuantumRange(depth);
1637 #if defined(_OPENMP) && (_OPENMP >= 200203)
1638 #pragma omp parallel for schedule(static,1) shared(status)
1640 for (i=0; i < (long) image->colors; i++)
1642 if ((channel & RedChannel) != 0)
1643 p->red=ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),range);
1644 if ((channel & GreenChannel) != 0)
1645 p->green=ScaleAnyToQuantum(ScaleQuantumToAny(p->green,range),range);
1646 if ((channel & BlueChannel) != 0)
1647 p->blue=ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),range);
1648 if ((channel & OpacityChannel) != 0)
1649 p->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,range),