blob: d1b8dabb11320bb58e322160e25e1edd1a165880 [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% %
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
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
91/*
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93% %
94% %
95% %
cristy316d5172009-09-17 19:31:25 +000096% 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
120static 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
133static 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
163MagickExport 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
342/*
343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344% %
345% %
346% %
cristy3ed852e2009-09-05 21:47:34 +0000347+ 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*/
370MagickExport 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);
cristy629a6c72009-09-13 23:28:22 +0000421#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000422 #pragma omp parallel for schedule(static,1) shared(status)
cristy3ed852e2009-09-05 21:47:34 +0000423#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
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
527MagickExport unsigned long GetImageDepth(const Image *image,
528 ExceptionInfo *exception)
529{
530 return(GetImageChannelDepth(image,AllChannels,exception));
531}
532
533MagickExport 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;
cristy629a6c72009-09-13 23:28:22 +0000577#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000578 #pragma omp parallel for schedule(static,1) shared(status)
cristy3ed852e2009-09-05 21:47:34 +0000579#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);
cristy629a6c72009-09-13 23:28:22 +0000618#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000619 #pragma omp parallel for schedule(static,1) shared(status)
cristy3ed852e2009-09-05 21:47:34 +0000620#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
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
719MagickExport 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
725MagickExport 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
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
780MagickExport 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
791MagickExport 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
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
904MagickExport 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
915MagickExport 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
1061MagickExport MagickBooleanType GetImageRange(const Image *image,
1062 double *minima,double *maxima,ExceptionInfo *exception)
1063{
1064 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1065}
1066
1067MagickExport 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
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
1178static inline double MagickMax(const double x,const double y)
1179{
1180 if (x > y)
1181 return(x);
1182 return(y);
1183}
1184
1185static inline double MagickMin(const double x,const double y)
1186{
1187 if (x < y)
1188 return(x);
1189 return(y);
1190}
1191
1192MagickExport 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
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*/
1483MagickExport 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
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
1536MagickExport MagickBooleanType SetImageDepth(Image *image,
1537 const unsigned long depth)
1538{
1539 return(SetImageChannelDepth(image,AllChannels,depth));
1540}
1541
1542MagickExport 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);
cristy629a6c72009-09-13 23:28:22 +00001577#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00001578 #pragma omp parallel for schedule(static,1) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001579#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);
cristy629a6c72009-09-13 23:28:22 +00001636#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00001637 #pragma omp parallel for schedule(static,1) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001638#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}