blob: 94595d3e3aedca41135eb37ad3b25a338547a565 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "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"
cristyacd5a392009-09-18 00:04:22 +000075#include "magick/monitor-private.h"
cristy3ed852e2009-09-05 21:47:34 +000076#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"
91
92/*
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94% %
95% %
96% %
cristy316d5172009-09-17 19:31:25 +000097% A v e r a g e I m a g e s %
98% %
99% %
100% %
101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102%
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.
108%
109% The format of the AverageImages method is:
110%
111% Image *AverageImages(Image *image,ExceptionInfo *exception)
112%
113% A description of each parameter follows:
114%
115% o image: the image sequence.
116%
117% o exception: return any errors or warnings in this structure.
118%
119*/
120
121static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
122{
123 register long
124 i;
125
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);
131 return(pixels);
132}
133
134static MagickPixelPacket **AcquirePixelThreadSet(const Image *image)
135{
136 register long
137 i,
138 j;
139
140 MagickPixelPacket
141 **pixels;
142
143 unsigned long
144 number_threads;
145
146 number_threads=GetOpenMPMaximumThreads();
147 pixels=(MagickPixelPacket **) AcquireAlignedMemory(number_threads,
148 sizeof(*pixels));
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++)
153 {
154 pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(image->columns,
155 sizeof(**pixels));
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]);
160 }
161 return(pixels);
162}
163
164MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
165{
166#define AverageImageTag "Average/Image"
167
168 CacheView
169 *average_view;
170
171 const Image
172 *next;
173
174 Image
175 *average_image;
176
177 long
178 progress,
179 y;
180
181 MagickBooleanType
182 status;
183
184 MagickPixelPacket
cristyfa112112010-01-04 17:48:07 +0000185 **restrict average_pixels,
cristy316d5172009-09-17 19:31:25 +0000186 zero;
187
188 unsigned long
189 number_images;
190
191 /*
192 Ensure the image are the same size.
193 */
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");
203 /*
204 Initialize average next attributes.
205 */
206 average_image=CloneImage(image,image->columns,image->rows,MagickTrue,
207 exception);
208 if (average_image == (Image *) NULL)
209 return((Image *) NULL);
210 if (SetImageStorageClass(average_image,DirectClass) == MagickFalse)
211 {
212 InheritException(exception,&average_image->exception);
213 average_image=DestroyImage(average_image);
214 return((Image *) NULL);
215 }
216 average_pixels=AcquirePixelThreadSet(image);
217 if (average_pixels == (MagickPixelPacket **) NULL)
218 {
219 average_image=DestroyImage(average_image);
220 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
221 }
222 /*
223 Average image pixels.
224 */
225 status=MagickTrue;
226 progress=0;
227 GetMagickPixelPacket(image,&zero);
228 number_images=GetImageListLength(image);
229 average_view=AcquireCacheView(average_image);
cristyb5d5f722009-11-04 03:03:49 +0000230#if defined(MAGICKCORE_OPENMP_SUPPORT)
231 #pragma omp parallel for schedule(dynamic) shared(progress,status)
cristy316d5172009-09-17 19:31:25 +0000232#endif
233 for (y=0; y < (long) average_image->rows; y++)
234 {
235 CacheView
236 *image_view;
237
238 const Image
239 *next;
240
241 MagickPixelPacket
242 pixel;
243
244 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000245 *restrict average_indexes;
cristy316d5172009-09-17 19:31:25 +0000246
247 register long
248 i,
249 id,
250 x;
251
252 register MagickPixelPacket
253 *average_pixel;
254
255 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000256 *restrict q;
cristy316d5172009-09-17 19:31:25 +0000257
258 if (status == MagickFalse)
259 continue;
260 q=QueueCacheViewAuthenticPixels(average_view,0,y,average_image->columns,1,
261 exception);
262 if (q == (PixelPacket *) NULL)
263 {
264 status=MagickFalse;
265 continue;
266 }
267 average_indexes=GetCacheViewAuthenticIndexQueue(average_view);
268 pixel=zero;
269 id=GetOpenMPThreadId();
270 average_pixel=average_pixels[id];
271 for (x=0; x < (long) average_image->columns; x++)
272 average_pixel[x]=zero;
273 next=image;
274 for (i=0; i < (long) number_images; i++)
275 {
276 register const IndexPacket
277 *indexes;
278
279 register const PixelPacket
280 *p;
281
282 image_view=AcquireCacheView(next);
283 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
284 if (p == (const PixelPacket *) NULL)
285 {
286 image_view=DestroyCacheView(image_view);
287 break;
288 }
289 indexes=GetCacheViewVirtualIndexQueue(image_view);
290 for (x=0; x < (long) next->columns; x++)
291 {
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;
299 p++;
300 }
301 image_view=DestroyCacheView(image_view);
302 next=GetNextImageInList(next);
303 }
304 for (x=0; x < (long) average_image->columns; x++)
305 {
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);
318 q++;
319 }
320 if (SyncCacheViewAuthenticPixels(average_view,exception) == MagickFalse)
321 status=MagickFalse;
322 if (image->progress_monitor != (MagickProgressMonitor) NULL)
323 {
324 MagickBooleanType
325 proceed;
326
cristyb5d5f722009-11-04 03:03:49 +0000327#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy316d5172009-09-17 19:31:25 +0000328 #pragma omp critical (MagickCore_AverageImages)
329#endif
330 proceed=SetImageProgress(image,AverageImageTag,progress++,
331 average_image->rows);
332 if (proceed == MagickFalse)
333 status=MagickFalse;
334 }
335 }
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);
341}
342
343/*
344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345% %
346% %
347% %
cristy3ed852e2009-09-05 21:47:34 +0000348+ G e t I m a g e C h a n n e l E x t r e m a %
349% %
350% %
351% %
352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
353%
354% GetImageChannelExtrema() returns the extrema of one or more image channels.
355%
356% The format of the GetImageChannelExtrema method is:
357%
358% MagickBooleanType GetImageChannelExtrema(const Image *image,
359% const ChannelType channel,unsigned long *minima,unsigned long *maxima,
360% ExceptionInfo *exception)
361%
362% A description of each parameter follows:
363%
364% o image: the image.
365%
366% o channel: the channel.
367%
368% o minima: the minimum value in the channel.
369%
370% o maxima: the maximum value in the channel.
371%
372% o exception: return any errors or warnings in this structure.
373%
374*/
375
376MagickExport MagickBooleanType GetImageExtrema(const Image *image,
377 unsigned long *minima,unsigned long *maxima,ExceptionInfo *exception)
378{
379 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
380}
381
382MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
383 const ChannelType channel,unsigned long *minima,unsigned long *maxima,
384 ExceptionInfo *exception)
385{
386 double
387 max,
388 min;
389
390 MagickBooleanType
391 status;
392
393 assert(image != (Image *) NULL);
394 assert(image->signature == MagickSignature);
395 if (image->debug != MagickFalse)
396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
397 status=GetImageChannelRange(image,channel,&min,&max,exception);
398 *minima=(unsigned long) (min+0.5);
399 *maxima=(unsigned long) (max+0.5);
400 return(status);
401}
402
403/*
404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405% %
406% %
407% %
408% G e t I m a g e C h a n n e l M e a n %
409% %
410% %
411% %
412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413%
414% GetImageChannelMean() returns the mean and standard deviation of one or more
415% image channels.
416%
417% The format of the GetImageChannelMean method is:
418%
419% MagickBooleanType GetImageChannelMean(const Image *image,
420% const ChannelType channel,double *mean,double *standard_deviation,
421% ExceptionInfo *exception)
422%
423% A description of each parameter follows:
424%
425% o image: the image.
426%
427% o channel: the channel.
428%
429% o mean: the average value in the channel.
430%
431% o standard_deviation: the standard deviation of the channel.
432%
433% o exception: return any errors or warnings in this structure.
434%
435*/
436
437MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
438 double *standard_deviation,ExceptionInfo *exception)
439{
440 MagickBooleanType
441 status;
442
443 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
444 exception);
445 return(status);
446}
447
448MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
449 const ChannelType channel,double *mean,double *standard_deviation,
450 ExceptionInfo *exception)
451{
452 double
453 area;
454
455 long
456 y;
457
458 assert(image != (Image *) NULL);
459 assert(image->signature == MagickSignature);
460 if (image->debug != MagickFalse)
461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
462 *mean=0.0;
463 *standard_deviation=0.0;
464 area=0.0;
465 for (y=0; y < (long) image->rows; y++)
466 {
467 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000468 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000469
470 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000471 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000472
473 register long
474 x;
475
476 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
477 if (p == (const PixelPacket *) NULL)
478 break;
479 indexes=GetVirtualIndexQueue(image);
480 for (x=0; x < (long) image->columns; x++)
481 {
482 if ((channel & RedChannel) != 0)
483 {
cristyc4c8d132010-01-07 01:58:38 +0000484 *mean+=GetRedSample(p);
485 *standard_deviation+=(double) p->red*GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000486 area++;
487 }
488 if ((channel & GreenChannel) != 0)
489 {
cristyc4c8d132010-01-07 01:58:38 +0000490 *mean+=GetGreenSample(p);
491 *standard_deviation+=(double) p->green*GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000492 area++;
493 }
494 if ((channel & BlueChannel) != 0)
495 {
cristyc4c8d132010-01-07 01:58:38 +0000496 *mean+=GetBlueSample(p);
497 *standard_deviation+=(double) p->blue*GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000498 area++;
499 }
500 if ((channel & OpacityChannel) != 0)
501 {
cristyc4c8d132010-01-07 01:58:38 +0000502 *mean+=GetOpacitySample(p);
503 *standard_deviation+=(double) p->opacity*GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +0000504 area++;
505 }
506 if (((channel & IndexChannel) != 0) &&
507 (image->colorspace == CMYKColorspace))
508 {
509 *mean+=indexes[x];
510 *standard_deviation+=(double) indexes[x]*indexes[x];
511 area++;
512 }
513 p++;
514 }
515 }
516 if (y < (long) image->rows)
517 return(MagickFalse);
518 if (area != 0)
519 {
520 *mean/=area;
521 *standard_deviation/=area;
522 }
523 *standard_deviation=sqrt(*standard_deviation-(*mean*(*mean)));
524 return(y == (long) image->rows ? MagickTrue : MagickFalse);
525}
526
527/*
528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529% %
530% %
531% %
532% G e t I m a g e C h a n n e l K u r t o s i s %
533% %
534% %
535% %
536%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537%
538% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
539% image channels.
540%
541% The format of the GetImageChannelKurtosis method is:
542%
543% MagickBooleanType GetImageChannelKurtosis(const Image *image,
544% const ChannelType channel,double *kurtosis,double *skewness,
545% ExceptionInfo *exception)
546%
547% A description of each parameter follows:
548%
549% o image: the image.
550%
551% o channel: the channel.
552%
553% o kurtosis: the kurtosis of the channel.
554%
555% o skewness: the skewness of the channel.
556%
557% o exception: return any errors or warnings in this structure.
558%
559*/
560
561MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
562 double *kurtosis,double *skewness,ExceptionInfo *exception)
563{
564 MagickBooleanType
565 status;
566
567 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
568 exception);
569 return(status);
570}
571
572MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
573 const ChannelType channel,double *kurtosis,double *skewness,
574 ExceptionInfo *exception)
575{
576 double
577 area,
578 mean,
579 standard_deviation,
580 sum_squares,
581 sum_cubes,
582 sum_fourth_power;
583
584 long
585 y;
586
587 assert(image != (Image *) NULL);
588 assert(image->signature == MagickSignature);
589 if (image->debug != MagickFalse)
590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
591 *kurtosis=0.0;
592 *skewness=0.0;
593 area=0.0;
594 mean=0.0;
595 standard_deviation=0.0;
596 sum_squares=0.0;
597 sum_cubes=0.0;
598 sum_fourth_power=0.0;
599 for (y=0; y < (long) image->rows; y++)
600 {
601 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000602 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000603
604 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000605 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000606
607 register long
608 x;
609
610 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
611 if (p == (const PixelPacket *) NULL)
612 break;
613 indexes=GetVirtualIndexQueue(image);
614 for (x=0; x < (long) image->columns; x++)
615 {
616 if ((channel & RedChannel) != 0)
617 {
cristyc4c8d132010-01-07 01:58:38 +0000618 mean+=GetRedSample(p);
619 sum_squares+=(double) p->red*GetRedSample(p);
620 sum_cubes+=(double) p->red*p->red*GetRedSample(p);
621 sum_fourth_power+=(double) p->red*p->red*p->red*GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000622 area++;
623 }
624 if ((channel & GreenChannel) != 0)
625 {
cristyc4c8d132010-01-07 01:58:38 +0000626 mean+=GetGreenSample(p);
627 sum_squares+=(double) p->green*GetGreenSample(p);
628 sum_cubes+=(double) p->green*p->green*GetGreenSample(p);
629 sum_fourth_power+=(double) p->green*p->green*p->green*GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000630 area++;
631 }
632 if ((channel & BlueChannel) != 0)
633 {
cristyc4c8d132010-01-07 01:58:38 +0000634 mean+=GetBlueSample(p);
635 sum_squares+=(double) p->blue*GetBlueSample(p);
636 sum_cubes+=(double) p->blue*p->blue*GetBlueSample(p);
637 sum_fourth_power+=(double) p->blue*p->blue*p->blue*GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000638 area++;
639 }
640 if ((channel & OpacityChannel) != 0)
641 {
cristyc4c8d132010-01-07 01:58:38 +0000642 mean+=GetOpacitySample(p);
643 sum_squares+=(double) p->opacity*GetOpacitySample(p);
644 sum_cubes+=(double) p->opacity*p->opacity*GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +0000645 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
cristyc4c8d132010-01-07 01:58:38 +0000646 GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +0000647 area++;
648 }
649 if (((channel & IndexChannel) != 0) &&
650 (image->colorspace == CMYKColorspace))
651 {
652 mean+=indexes[x];
653 sum_squares+=(double) indexes[x]*indexes[x];
654 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
655 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
656 indexes[x];
657 area++;
658 }
659 p++;
660 }
661 }
662 if (y < (long) image->rows)
663 return(MagickFalse);
664 if (area != 0.0)
665 {
666 mean/=area;
667 sum_squares/=area;
668 sum_cubes/=area;
669 sum_fourth_power/=area;
670 }
671 standard_deviation=sqrt(sum_squares-(mean*mean));
672 if (standard_deviation != 0.0)
673 {
674 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
675 3.0*mean*mean*mean*mean;
676 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
677 standard_deviation;
678 *kurtosis-=3.0;
679 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
680 *skewness/=standard_deviation*standard_deviation*standard_deviation;
681 }
682 return(y == (long) image->rows ? MagickTrue : MagickFalse);
683}
684
685/*
686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687% %
688% %
689% %
690% G e t I m a g e C h a n n e l R a n g e %
691% %
692% %
693% %
694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695%
696% GetImageChannelRange() returns the range of one or more image channels.
697%
698% The format of the GetImageChannelRange method is:
699%
700% MagickBooleanType GetImageChannelRange(const Image *image,
701% const ChannelType channel,double *minima,double *maxima,
702% ExceptionInfo *exception)
703%
704% A description of each parameter follows:
705%
706% o image: the image.
707%
708% o channel: the channel.
709%
710% o minima: the minimum value in the channel.
711%
712% o maxima: the maximum value in the channel.
713%
714% o exception: return any errors or warnings in this structure.
715%
716*/
717
718MagickExport MagickBooleanType GetImageRange(const Image *image,
719 double *minima,double *maxima,ExceptionInfo *exception)
720{
721 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
722}
723
724MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
725 const ChannelType channel,double *minima,double *maxima,
726 ExceptionInfo *exception)
727{
728 long
729 y;
730
731 MagickPixelPacket
732 pixel;
733
734 assert(image != (Image *) NULL);
735 assert(image->signature == MagickSignature);
736 if (image->debug != MagickFalse)
737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
738 *maxima=(-1.0E-37);
739 *minima=1.0E+37;
740 GetMagickPixelPacket(image,&pixel);
741 for (y=0; y < (long) image->rows; y++)
742 {
743 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000744 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000745
746 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000747 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000748
749 register long
750 x;
751
752 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
753 if (p == (const PixelPacket *) NULL)
754 break;
755 indexes=GetVirtualIndexQueue(image);
756 for (x=0; x < (long) image->columns; x++)
757 {
758 SetMagickPixelPacket(image,p,indexes+x,&pixel);
759 if ((channel & RedChannel) != 0)
760 {
761 if (pixel.red < *minima)
762 *minima=(double) pixel.red;
763 if (pixel.red > *maxima)
764 *maxima=(double) pixel.red;
765 }
766 if ((channel & GreenChannel) != 0)
767 {
768 if (pixel.green < *minima)
769 *minima=(double) pixel.green;
770 if (pixel.green > *maxima)
771 *maxima=(double) pixel.green;
772 }
773 if ((channel & BlueChannel) != 0)
774 {
775 if (pixel.blue < *minima)
776 *minima=(double) pixel.blue;
777 if (pixel.blue > *maxima)
778 *maxima=(double) pixel.blue;
779 }
780 if ((channel & OpacityChannel) != 0)
781 {
782 if (pixel.opacity < *minima)
783 *minima=(double) pixel.opacity;
784 if (pixel.opacity > *maxima)
785 *maxima=(double) pixel.opacity;
786 }
787 if (((channel & IndexChannel) != 0) &&
788 (image->colorspace == CMYKColorspace))
789 {
790 if ((double) indexes[x] < *minima)
791 *minima=(double) indexes[x];
792 if ((double) indexes[x] > *maxima)
793 *maxima=(double) indexes[x];
794 }
795 p++;
796 }
797 }
798 return(y == (long) image->rows ? MagickTrue : MagickFalse);
799}
800
801/*
802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803% %
804% %
805% %
806% 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 %
807% %
808% %
809% %
810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811%
812% GetImageChannelStatistics() returns statistics for each channel in the
813% image. The statistics include the channel depth, its minima, maxima, mean,
814% standard deviation, kurtosis and skewness. You can access the red channel
815% mean, for example, like this:
816%
817% channel_statistics=GetImageChannelStatistics(image,excepton);
818% red_mean=channel_statistics[RedChannel].mean;
819%
820% Use MagickRelinquishMemory() to free the statistics buffer.
821%
822% The format of the GetImageChannelStatistics method is:
823%
824% ChannelStatistics *GetImageChannelStatistics(const Image *image,
825% ExceptionInfo *exception)
826%
827% A description of each parameter follows:
828%
829% o image: the image.
830%
831% o exception: return any errors or warnings in this structure.
832%
833*/
834
835static inline double MagickMax(const double x,const double y)
836{
837 if (x > y)
838 return(x);
839 return(y);
840}
841
842static inline double MagickMin(const double x,const double y)
843{
844 if (x < y)
845 return(x);
846 return(y);
847}
848
849MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
850 ExceptionInfo *exception)
851{
852 ChannelStatistics
853 *channel_statistics;
854
855 double
856 area,
857 sum_squares,
858 sum_cubes;
859
860 long
861 y;
862
863 MagickStatusType
864 status;
865
866 QuantumAny
867 range;
868
869 register long
870 i;
871
872 size_t
873 length;
874
875 unsigned long
876 channels,
877 depth;
878
879 assert(image != (Image *) NULL);
880 assert(image->signature == MagickSignature);
881 if (image->debug != MagickFalse)
882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
883 length=AllChannels+1UL;
884 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
885 sizeof(*channel_statistics));
886 if (channel_statistics == (ChannelStatistics *) NULL)
887 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
888 (void) ResetMagickMemory(channel_statistics,0,length*
889 sizeof(*channel_statistics));
890 for (i=0; i <= AllChannels; i++)
891 {
892 channel_statistics[i].depth=1;
893 channel_statistics[i].maxima=(-1.0E-37);
894 channel_statistics[i].minima=1.0E+37;
895 channel_statistics[i].mean=0.0;
896 channel_statistics[i].standard_deviation=0.0;
897 channel_statistics[i].kurtosis=0.0;
898 channel_statistics[i].skewness=0.0;
899 }
900 for (y=0; y < (long) image->rows; y++)
901 {
902 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000903 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000904
905 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000906 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000907
908 register long
909 x;
910
911 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
912 if (p == (const PixelPacket *) NULL)
913 break;
914 indexes=GetVirtualIndexQueue(image);
915 for (x=0; x < (long) image->columns; )
916 {
917 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
918 {
919 depth=channel_statistics[RedChannel].depth;
920 range=GetQuantumRange(depth);
921 status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
922 range) ? MagickTrue : MagickFalse;
923 if (status != MagickFalse)
924 {
925 channel_statistics[RedChannel].depth++;
926 continue;
927 }
928 }
929 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
930 {
931 depth=channel_statistics[GreenChannel].depth;
932 range=GetQuantumRange(depth);
933 status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
934 range),range) ? MagickTrue : MagickFalse;
935 if (status != MagickFalse)
936 {
937 channel_statistics[GreenChannel].depth++;
938 continue;
939 }
940 }
941 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
942 {
943 depth=channel_statistics[BlueChannel].depth;
944 range=GetQuantumRange(depth);
945 status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
946 range),range) ? MagickTrue : MagickFalse;
947 if (status != MagickFalse)
948 {
949 channel_statistics[BlueChannel].depth++;
950 continue;
951 }
952 }
953 if (image->matte != MagickFalse)
954 {
955 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
956 {
957 depth=channel_statistics[OpacityChannel].depth;
958 range=GetQuantumRange(depth);
959 status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
960 p->opacity,range),range) ? MagickTrue : MagickFalse;
961 if (status != MagickFalse)
962 {
963 channel_statistics[OpacityChannel].depth++;
964 continue;
965 }
966 }
967 }
968 if (image->colorspace == CMYKColorspace)
969 {
970 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
971 {
972 depth=channel_statistics[BlackChannel].depth;
973 range=GetQuantumRange(depth);
974 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
975 indexes[x],range),range) ? MagickTrue : MagickFalse;
976 if (status != MagickFalse)
977 {
978 channel_statistics[BlackChannel].depth++;
979 continue;
980 }
981 }
982 }
983 if ((double) p->red < channel_statistics[RedChannel].minima)
cristyc4c8d132010-01-07 01:58:38 +0000984 channel_statistics[RedChannel].minima=(double) GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000985 if ((double) p->red > channel_statistics[RedChannel].maxima)
cristyc4c8d132010-01-07 01:58:38 +0000986 channel_statistics[RedChannel].maxima=(double) GetRedSample(p);
987 channel_statistics[RedChannel].mean+=GetRedSample(p);
988 channel_statistics[RedChannel].standard_deviation+=(double) p->red*GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000989 channel_statistics[RedChannel].kurtosis+=(double) p->red*p->red*
cristyc4c8d132010-01-07 01:58:38 +0000990 p->red*GetRedSample(p);
991 channel_statistics[RedChannel].skewness+=(double) p->red*p->red*GetRedSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000992 if ((double) p->green < channel_statistics[GreenChannel].minima)
cristyc4c8d132010-01-07 01:58:38 +0000993 channel_statistics[GreenChannel].minima=(double) GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000994 if ((double) p->green > channel_statistics[GreenChannel].maxima)
cristyc4c8d132010-01-07 01:58:38 +0000995 channel_statistics[GreenChannel].maxima=(double) GetGreenSample(p);
996 channel_statistics[GreenChannel].mean+=GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000997 channel_statistics[GreenChannel].standard_deviation+=(double) p->green*
cristyc4c8d132010-01-07 01:58:38 +0000998 GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +0000999 channel_statistics[GreenChannel].kurtosis+=(double) p->green*p->green*
cristyc4c8d132010-01-07 01:58:38 +00001000 p->green*GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001001 channel_statistics[GreenChannel].skewness+=(double) p->green*p->green*
cristyc4c8d132010-01-07 01:58:38 +00001002 GetGreenSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001003 if ((double) p->blue < channel_statistics[BlueChannel].minima)
cristyc4c8d132010-01-07 01:58:38 +00001004 channel_statistics[BlueChannel].minima=(double) GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001005 if ((double) p->blue > channel_statistics[BlueChannel].maxima)
cristyc4c8d132010-01-07 01:58:38 +00001006 channel_statistics[BlueChannel].maxima=(double) GetBlueSample(p);
1007 channel_statistics[BlueChannel].mean+=GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001008 channel_statistics[BlueChannel].standard_deviation+=(double) p->blue*
cristyc4c8d132010-01-07 01:58:38 +00001009 GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001010 channel_statistics[BlueChannel].kurtosis+=(double) p->blue*p->blue*
cristyc4c8d132010-01-07 01:58:38 +00001011 p->blue*GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001012 channel_statistics[BlueChannel].skewness+=(double) p->blue*p->blue*
cristyc4c8d132010-01-07 01:58:38 +00001013 GetBlueSample(p);
cristy3ed852e2009-09-05 21:47:34 +00001014 if (image->matte != MagickFalse)
1015 {
1016 if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
cristyc4c8d132010-01-07 01:58:38 +00001017 channel_statistics[OpacityChannel].minima=(double) GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +00001018 if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
cristyc4c8d132010-01-07 01:58:38 +00001019 channel_statistics[OpacityChannel].maxima=(double) GetOpacitySample(p);
1020 channel_statistics[OpacityChannel].mean+=GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +00001021 channel_statistics[OpacityChannel].standard_deviation+=(double)
cristyc4c8d132010-01-07 01:58:38 +00001022 p->opacity*GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +00001023 channel_statistics[OpacityChannel].kurtosis+=(double) p->opacity*
cristyc4c8d132010-01-07 01:58:38 +00001024 p->opacity*p->opacity*GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +00001025 channel_statistics[OpacityChannel].skewness+=(double) p->opacity*
cristyc4c8d132010-01-07 01:58:38 +00001026 p->opacity*GetOpacitySample(p);
cristy3ed852e2009-09-05 21:47:34 +00001027 }
1028 if (image->colorspace == CMYKColorspace)
1029 {
1030 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1031 channel_statistics[BlackChannel].minima=(double) indexes[x];
1032 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1033 channel_statistics[BlackChannel].maxima=(double) indexes[x];
1034 channel_statistics[BlackChannel].mean+=indexes[x];
1035 channel_statistics[BlackChannel].standard_deviation+=(double)
1036 indexes[x]*indexes[x];
1037 channel_statistics[BlackChannel].kurtosis+=(double) indexes[x]*
1038 indexes[x]*indexes[x]*indexes[x];
1039 channel_statistics[BlackChannel].skewness+=(double) indexes[x]*
1040 indexes[x]*indexes[x];
1041 }
1042 x++;
1043 p++;
1044 }
1045 }
1046 area=(double) image->columns*image->rows;
1047 for (i=0; i < AllChannels; i++)
1048 {
1049 channel_statistics[i].mean/=area;
1050 channel_statistics[i].standard_deviation/=area;
1051 channel_statistics[i].kurtosis/=area;
1052 channel_statistics[i].skewness/=area;
1053 }
1054 for (i=0; i < AllChannels; i++)
1055 {
1056 channel_statistics[AllChannels].depth=(unsigned long) MagickMax((double)
1057 channel_statistics[AllChannels].depth,(double)
1058 channel_statistics[i].depth);
1059 channel_statistics[AllChannels].minima=MagickMin(
1060 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1061 channel_statistics[AllChannels].maxima=MagickMax(
1062 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
1063 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
1064 channel_statistics[AllChannels].standard_deviation+=
1065 channel_statistics[i].standard_deviation;
1066 channel_statistics[AllChannels].kurtosis+=channel_statistics[i].kurtosis;
1067 channel_statistics[AllChannels].skewness+=channel_statistics[i].skewness;
1068 }
1069 channels=4;
1070 if (image->colorspace == CMYKColorspace)
1071 channels++;
1072 channel_statistics[AllChannels].mean/=channels;
1073 channel_statistics[AllChannels].standard_deviation/=channels;
1074 channel_statistics[AllChannels].kurtosis/=channels;
1075 channel_statistics[AllChannels].skewness/=channels;
1076 for (i=0; i <= AllChannels; i++)
1077 {
1078 sum_squares=0.0;
1079 sum_squares=channel_statistics[i].standard_deviation;
1080 sum_cubes=0.0;
1081 sum_cubes=channel_statistics[i].skewness;
1082 channel_statistics[i].standard_deviation=sqrt(
1083 channel_statistics[i].standard_deviation-
1084 (channel_statistics[i].mean*channel_statistics[i].mean));
1085 if (channel_statistics[i].standard_deviation == 0.0)
1086 {
1087 channel_statistics[i].kurtosis=0.0;
1088 channel_statistics[i].skewness=0.0;
1089 }
1090 else
1091 {
1092 channel_statistics[i].skewness=(channel_statistics[i].skewness-
1093 3.0*channel_statistics[i].mean*sum_squares+
1094 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1095 channel_statistics[i].mean)/
1096 (channel_statistics[i].standard_deviation*
1097 channel_statistics[i].standard_deviation*
1098 channel_statistics[i].standard_deviation);
1099 channel_statistics[i].kurtosis=(channel_statistics[i].kurtosis-
1100 4.0*channel_statistics[i].mean*sum_cubes+
1101 6.0*channel_statistics[i].mean*channel_statistics[i].mean*sum_squares-
1102 3.0*channel_statistics[i].mean*channel_statistics[i].mean*
1103 1.0*channel_statistics[i].mean*channel_statistics[i].mean)/
1104 (channel_statistics[i].standard_deviation*
1105 channel_statistics[i].standard_deviation*
1106 channel_statistics[i].standard_deviation*
1107 channel_statistics[i].standard_deviation)-3.0;
1108 }
1109 }
1110 return(channel_statistics);
1111}