blob: 6408974e8b7740c54e9451bd9efea26e9ea8ea47 [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% %
cristy3e2860c2010-01-24 01:36:30 +000013% MagickCore Image Statistical Methods %
cristy3ed852e2009-09-05 21:47:34 +000014% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
cristy351842f2010-03-07 15:27:38 +000082#include "magick/random-private.h"
cristy3ed852e2009-09-05 21:47:34 +000083#include "magick/segment.h"
84#include "magick/semaphore.h"
85#include "magick/signature-private.h"
86#include "magick/statistic.h"
87#include "magick/string_.h"
88#include "magick/thread-private.h"
89#include "magick/timer.h"
90#include "magick/utility.h"
91#include "magick/version.h"
92
93/*
94%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95% %
96% %
97% %
cristyd18ae7c2010-03-07 17:39:52 +000098% E v a l u a t e I m a g e %
cristy316d5172009-09-17 19:31:25 +000099% %
100% %
101% %
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103%
cristyd18ae7c2010-03-07 17:39:52 +0000104% EvaluateImage() applies a value to the image with an arithmetic, relational,
105% or logical operator to an image. Use these operations to lighten or darken
106% an image, to increase or decrease contrast in an image, or to produce the
107% "negative" of an image.
cristy316d5172009-09-17 19:31:25 +0000108%
cristyd18ae7c2010-03-07 17:39:52 +0000109% The format of the EvaluateImageChannel method is:
cristy316d5172009-09-17 19:31:25 +0000110%
cristyd18ae7c2010-03-07 17:39:52 +0000111% MagickBooleanType EvaluateImage(Image *image,
112% const MagickEvaluateOperator op,const double value,
113% ExceptionInfo *exception)
114% MagickBooleanType EvaluateImages(Image *images,
115% const MagickEvaluateOperator op,const double value,
116% ExceptionInfo *exception)
117% MagickBooleanType EvaluateImageChannel(Image *image,
118% const ChannelType channel,const MagickEvaluateOperator op,
119% const double value,ExceptionInfo *exception)
cristy316d5172009-09-17 19:31:25 +0000120%
121% A description of each parameter follows:
122%
cristyd18ae7c2010-03-07 17:39:52 +0000123% o image: the image.
124%
125% o channel: the channel.
126%
127% o op: A channel op.
128%
129% o value: A value value.
cristy316d5172009-09-17 19:31:25 +0000130%
131% o exception: return any errors or warnings in this structure.
132%
133*/
134
135static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
136{
cristybb503372010-05-27 20:51:26 +0000137 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000138 i;
139
140 assert(pixels != (MagickPixelPacket **) NULL);
cristybb503372010-05-27 20:51:26 +0000141 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy316d5172009-09-17 19:31:25 +0000142 if (pixels[i] != (MagickPixelPacket *) NULL)
143 pixels[i]=(MagickPixelPacket *) RelinquishMagickMemory(pixels[i]);
cristyb41ee102010-10-04 16:46:15 +0000144 pixels=(MagickPixelPacket **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000145 return(pixels);
146}
147
cristy08a3d702010-11-28 01:57:36 +0000148static MagickPixelPacket **AcquirePixelThreadSet(const Image *image,
149 const size_t number_images)
cristy316d5172009-09-17 19:31:25 +0000150{
cristybb503372010-05-27 20:51:26 +0000151 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000152 i,
153 j;
154
155 MagickPixelPacket
156 **pixels;
157
cristybb503372010-05-27 20:51:26 +0000158 size_t
cristy08a3d702010-11-28 01:57:36 +0000159 length,
cristy316d5172009-09-17 19:31:25 +0000160 number_threads;
161
162 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +0000163 pixels=(MagickPixelPacket **) AcquireQuantumMemory(number_threads,
cristy316d5172009-09-17 19:31:25 +0000164 sizeof(*pixels));
165 if (pixels == (MagickPixelPacket **) NULL)
166 return((MagickPixelPacket **) NULL);
167 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000168 for (i=0; i < (ssize_t) number_threads; i++)
cristy316d5172009-09-17 19:31:25 +0000169 {
cristy08a3d702010-11-28 01:57:36 +0000170 length=image->columns;
171 if (length < number_images)
172 length=number_images;
173 pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(length,
cristy316d5172009-09-17 19:31:25 +0000174 sizeof(**pixels));
175 if (pixels[i] == (MagickPixelPacket *) NULL)
176 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000177 for (j=0; j < (ssize_t) length; j++)
cristy316d5172009-09-17 19:31:25 +0000178 GetMagickPixelPacket(image,&pixels[i][j]);
179 }
180 return(pixels);
181}
182
cristy351842f2010-03-07 15:27:38 +0000183static inline double MagickMax(const double x,const double y)
184{
185 if (x > y)
186 return(x);
187 return(y);
188}
189
cristy08a3d702010-11-28 01:57:36 +0000190#if defined(__cplusplus) || defined(c_plusplus)
191extern "C" {
192#endif
193
194static int IntensityCompare(const void *x,const void *y)
195{
196 const MagickPixelPacket
197 *color_1,
198 *color_2;
199
200 int
201 intensity;
202
203 color_1=(const MagickPixelPacket *) x;
204 color_2=(const MagickPixelPacket *) y;
205 intensity=(int) MagickPixelIntensity(color_2)-
206 (int) MagickPixelIntensity(color_1);
207 return(intensity);
208}
209
210#if defined(__cplusplus) || defined(c_plusplus)
211}
212#endif
213
cristy351842f2010-03-07 15:27:38 +0000214static inline double MagickMin(const double x,const double y)
215{
216 if (x < y)
217 return(x);
218 return(y);
219}
220
cristyd18ae7c2010-03-07 17:39:52 +0000221static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
222 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000223{
224 MagickRealType
225 result;
226
227 result=0.0;
228 switch (op)
229 {
230 case UndefinedEvaluateOperator:
231 break;
cristy33aaed22010-08-11 18:10:50 +0000232 case AbsEvaluateOperator:
233 {
234 result=(MagickRealType) fabs((double) (pixel+value));
235 break;
236 }
cristy351842f2010-03-07 15:27:38 +0000237 case AddEvaluateOperator:
238 {
239 result=(MagickRealType) (pixel+value);
240 break;
241 }
242 case AddModulusEvaluateOperator:
243 {
244 /*
245 This returns a 'floored modulus' of the addition which is a
246 positive result. It differs from % or fmod() which returns a
247 'truncated modulus' result, where floor() is replaced by trunc()
248 and could return a negative result (which is clipped).
249 */
250 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000251 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000252 break;
253 }
254 case AndEvaluateOperator:
255 {
cristy9fe8cd72010-10-19 01:24:07 +0000256 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000257 break;
258 }
259 case CosineEvaluateOperator:
260 {
261 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
262 QuantumScale*pixel*value))+0.5));
263 break;
264 }
265 case DivideEvaluateOperator:
266 {
267 result=pixel/(value == 0.0 ? 1.0 : value);
268 break;
269 }
cristy9fe8cd72010-10-19 01:24:07 +0000270 case ExponentialEvaluateOperator:
271 {
272 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
273 pixel)));
274 break;
275 }
cristy351842f2010-03-07 15:27:38 +0000276 case GaussianNoiseEvaluateOperator:
277 {
278 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
279 GaussianNoise,value);
280 break;
281 }
282 case ImpulseNoiseEvaluateOperator:
283 {
284 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
285 ImpulseNoise,value);
286 break;
287 }
288 case LaplacianNoiseEvaluateOperator:
289 {
290 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
291 LaplacianNoise,value);
292 break;
293 }
294 case LeftShiftEvaluateOperator:
295 {
cristy9fe8cd72010-10-19 01:24:07 +0000296 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000297 break;
298 }
299 case LogEvaluateOperator:
300 {
301 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
302 pixel+1.0))/log((double) (value+1.0)));
303 break;
304 }
305 case MaxEvaluateOperator:
306 {
307 result=(MagickRealType) MagickMax((double) pixel,value);
308 break;
309 }
310 case MeanEvaluateOperator:
311 {
cristy125a5a32010-05-07 13:30:52 +0000312 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000313 break;
314 }
cristy08a3d702010-11-28 01:57:36 +0000315 case MedianEvaluateOperator:
316 {
317 result=(MagickRealType) (pixel+value);
318 break;
319 }
cristy351842f2010-03-07 15:27:38 +0000320 case MinEvaluateOperator:
321 {
322 result=(MagickRealType) MagickMin((double) pixel,value);
323 break;
324 }
325 case MultiplicativeNoiseEvaluateOperator:
326 {
327 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
328 MultiplicativeGaussianNoise,value);
329 break;
330 }
331 case MultiplyEvaluateOperator:
332 {
333 result=(MagickRealType) (value*pixel);
334 break;
335 }
336 case OrEvaluateOperator:
337 {
cristy9fe8cd72010-10-19 01:24:07 +0000338 result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000339 break;
340 }
341 case PoissonNoiseEvaluateOperator:
342 {
343 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
344 PoissonNoise,value);
345 break;
346 }
347 case PowEvaluateOperator:
348 {
349 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
350 (double) value));
351 break;
352 }
353 case RightShiftEvaluateOperator:
354 {
cristy9fe8cd72010-10-19 01:24:07 +0000355 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000356 break;
357 }
358 case SetEvaluateOperator:
359 {
360 result=value;
361 break;
362 }
363 case SineEvaluateOperator:
364 {
365 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
366 QuantumScale*pixel*value))+0.5));
367 break;
368 }
369 case SubtractEvaluateOperator:
370 {
371 result=(MagickRealType) (pixel-value);
372 break;
373 }
374 case ThresholdEvaluateOperator:
375 {
376 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
377 QuantumRange);
378 break;
379 }
380 case ThresholdBlackEvaluateOperator:
381 {
382 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
383 break;
384 }
385 case ThresholdWhiteEvaluateOperator:
386 {
387 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
388 pixel);
389 break;
390 }
391 case UniformNoiseEvaluateOperator:
392 {
393 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
394 UniformNoise,value);
395 break;
396 }
397 case XorEvaluateOperator:
398 {
cristy9fe8cd72010-10-19 01:24:07 +0000399 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000400 break;
401 }
402 }
cristyd18ae7c2010-03-07 17:39:52 +0000403 return(result);
cristy351842f2010-03-07 15:27:38 +0000404}
405
406MagickExport MagickBooleanType EvaluateImage(Image *image,
407 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
408{
409 MagickBooleanType
410 status;
411
412 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
413 return(status);
414}
415
cristyd18ae7c2010-03-07 17:39:52 +0000416MagickExport Image *EvaluateImages(const Image *images,
417 const MagickEvaluateOperator op,ExceptionInfo *exception)
418{
419#define EvaluateImageTag "Evaluate/Image"
420
421 CacheView
422 *evaluate_view;
423
424 const Image
425 *next;
426
427 Image
428 *evaluate_image;
429
cristyd18ae7c2010-03-07 17:39:52 +0000430 MagickBooleanType
431 status;
432
cristy5f959472010-05-27 22:19:46 +0000433 MagickOffsetType
434 progress;
435
cristyd18ae7c2010-03-07 17:39:52 +0000436 MagickPixelPacket
437 **restrict evaluate_pixels,
438 zero;
439
440 RandomInfo
441 **restrict random_info;
442
cristybb503372010-05-27 20:51:26 +0000443 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000444 number_images;
445
cristy5f959472010-05-27 22:19:46 +0000446 ssize_t
447 y;
448
cristyd18ae7c2010-03-07 17:39:52 +0000449 /*
450 Ensure the image are the same size.
451 */
452 assert(images != (Image *) NULL);
453 assert(images->signature == MagickSignature);
454 if (images->debug != MagickFalse)
455 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
456 assert(exception != (ExceptionInfo *) NULL);
457 assert(exception->signature == MagickSignature);
458 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
459 if ((next->columns != images->columns) || (next->rows != images->rows))
460 {
461 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
462 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
463 return((Image *) NULL);
464 }
465 /*
466 Initialize evaluate next attributes.
467 */
468 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
469 exception);
470 if (evaluate_image == (Image *) NULL)
471 return((Image *) NULL);
472 if (SetImageStorageClass(evaluate_image,DirectClass) == MagickFalse)
473 {
474 InheritException(exception,&evaluate_image->exception);
475 evaluate_image=DestroyImage(evaluate_image);
476 return((Image *) NULL);
477 }
cristy08a3d702010-11-28 01:57:36 +0000478 number_images=GetImageListLength(images);
479 evaluate_pixels=AcquirePixelThreadSet(images,number_images);
cristyd18ae7c2010-03-07 17:39:52 +0000480 if (evaluate_pixels == (MagickPixelPacket **) NULL)
481 {
482 evaluate_image=DestroyImage(evaluate_image);
483 (void) ThrowMagickException(exception,GetMagickModule(),
484 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
485 return((Image *) NULL);
486 }
487 /*
488 Evaluate image pixels.
489 */
490 status=MagickTrue;
491 progress=0;
492 GetMagickPixelPacket(images,&zero);
493 random_info=AcquireRandomInfoThreadSet();
cristyd18ae7c2010-03-07 17:39:52 +0000494 evaluate_view=AcquireCacheView(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000495 if (op == MedianEvaluateOperator)
cristyd18ae7c2010-03-07 17:39:52 +0000496#if defined(MAGICKCORE_OPENMP_SUPPORT)
497 #pragma omp parallel for schedule(dynamic) shared(progress,status)
498#endif
cristy08a3d702010-11-28 01:57:36 +0000499 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000500 {
cristy08a3d702010-11-28 01:57:36 +0000501 CacheView
502 *image_view;
cristyd18ae7c2010-03-07 17:39:52 +0000503
cristy08a3d702010-11-28 01:57:36 +0000504 const Image
505 *next;
cristyd18ae7c2010-03-07 17:39:52 +0000506
cristy08a3d702010-11-28 01:57:36 +0000507 const int
508 id = GetOpenMPThreadId();
509
510 MagickPixelPacket
511 pixel;
512
513 register IndexPacket
514 *restrict evaluate_indexes;
515
516 register MagickPixelPacket
517 *evaluate_pixel;
518
519 register PixelPacket
520 *restrict q;
521
522 register ssize_t
523 x;
524
525 if (status == MagickFalse)
526 continue;
527 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
528 1,exception);
529 if (q == (PixelPacket *) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000530 {
cristy08a3d702010-11-28 01:57:36 +0000531 status=MagickFalse;
532 continue;
cristyd18ae7c2010-03-07 17:39:52 +0000533 }
cristy08a3d702010-11-28 01:57:36 +0000534 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
535 pixel=zero;
536 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000537 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000538 {
cristy08a3d702010-11-28 01:57:36 +0000539 register ssize_t
540 i;
541
542 for (i=0; i < (ssize_t) number_images; i++)
543 evaluate_pixel[i]=zero;
544 next=images;
545 for (i=0; i < (ssize_t) number_images; i++)
546 {
547 register const IndexPacket
548 *indexes;
549
550 register const PixelPacket
551 *p;
552
553 image_view=AcquireCacheView(next);
554 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
555 if (p == (const PixelPacket *) NULL)
556 {
557 image_view=DestroyCacheView(image_view);
558 break;
559 }
560 indexes=GetCacheViewVirtualIndexQueue(image_view);
561 evaluate_pixel[i].red=ApplyEvaluateOperator(random_info[id],
562 p->red,op,evaluate_pixel[i].red);
563 evaluate_pixel[i].green=ApplyEvaluateOperator(random_info[id],
564 p->green,op,evaluate_pixel[i].green);
565 evaluate_pixel[i].blue=ApplyEvaluateOperator(random_info[id],
566 p->blue,op,evaluate_pixel[i].blue);
567 evaluate_pixel[i].opacity=ApplyEvaluateOperator(random_info[id],
568 p->opacity,op,evaluate_pixel[i].opacity);
569 if (evaluate_image->colorspace == CMYKColorspace)
570 evaluate_pixel[i].index=ApplyEvaluateOperator(random_info[id],
571 *indexes,op,evaluate_pixel[i].index);
572 image_view=DestroyCacheView(image_view);
573 next=GetNextImageInList(next);
574 }
575 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
576 IntensityCompare);
577 q->red=ClampToQuantum(evaluate_pixel[i/2].red);
578 q->green=ClampToQuantum(evaluate_pixel[i/2].green);
579 q->blue=ClampToQuantum(evaluate_pixel[i/2].blue);
580 if (evaluate_image->matte == MagickFalse)
581 q->opacity=ClampToQuantum(evaluate_pixel[i/2].opacity);
582 else
583 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[i/2].opacity);
584 if (evaluate_image->colorspace == CMYKColorspace)
585 evaluate_indexes[i]=ClampToQuantum(evaluate_pixel[i/2].index);
586 q++;
cristy125a5a32010-05-07 13:30:52 +0000587 }
cristy08a3d702010-11-28 01:57:36 +0000588 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
589 status=MagickFalse;
590 if (images->progress_monitor != (MagickProgressMonitor) NULL)
591 {
592 MagickBooleanType
593 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000594
595#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000596 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000597#endif
cristy08a3d702010-11-28 01:57:36 +0000598 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
599 evaluate_image->rows);
600 if (proceed == MagickFalse)
601 status=MagickFalse;
602 }
603 }
604 else
605#if defined(MAGICKCORE_OPENMP_SUPPORT)
606 #pragma omp parallel for schedule(dynamic) shared(progress,status)
607#endif
608 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
609 {
610 CacheView
611 *image_view;
612
613 const Image
614 *next;
615
616 const int
617 id = GetOpenMPThreadId();
618
619 MagickPixelPacket
620 pixel;
621
622 register IndexPacket
623 *restrict evaluate_indexes;
624
625 register ssize_t
626 i,
627 x;
628
629 register MagickPixelPacket
630 *evaluate_pixel;
631
632 register PixelPacket
633 *restrict q;
634
635 if (status == MagickFalse)
636 continue;
637 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
638 1,exception);
639 if (q == (PixelPacket *) NULL)
640 {
cristyd18ae7c2010-03-07 17:39:52 +0000641 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000642 continue;
643 }
644 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
645 pixel=zero;
646 evaluate_pixel=evaluate_pixels[id];
647 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
648 evaluate_pixel[x]=zero;
649 next=images;
650 for (i=0; i < (ssize_t) number_images; i++)
651 {
652 register const IndexPacket
653 *indexes;
654
655 register const PixelPacket
656 *p;
657
658 image_view=AcquireCacheView(next);
659 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
660 if (p == (const PixelPacket *) NULL)
661 {
662 image_view=DestroyCacheView(image_view);
663 break;
664 }
665 indexes=GetCacheViewVirtualIndexQueue(image_view);
666 for (x=0; x < (ssize_t) next->columns; x++)
667 {
668 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],
669 p->red,i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
670 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],
671 p->green,i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].green);
672 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],
673 p->blue,i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].blue);
674 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
675 p->opacity,i == 0 ? AddEvaluateOperator : op,
676 evaluate_pixel[x].opacity);
677 if (evaluate_image->colorspace == CMYKColorspace)
678 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
679 indexes[x],i == 0 ? AddEvaluateOperator : op,
680 evaluate_pixel[x].index);
681 p++;
682 }
683 image_view=DestroyCacheView(image_view);
684 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000685 }
cristy08a3d702010-11-28 01:57:36 +0000686 if (op == MeanEvaluateOperator)
687 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
688 {
689 evaluate_pixel[x].red/=number_images;
690 evaluate_pixel[x].green/=number_images;
691 evaluate_pixel[x].blue/=number_images;
692 evaluate_pixel[x].opacity/=number_images;
693 evaluate_pixel[x].index/=number_images;
694 }
695 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
696 {
697 q->red=ClampToQuantum(evaluate_pixel[x].red);
698 q->green=ClampToQuantum(evaluate_pixel[x].green);
699 q->blue=ClampToQuantum(evaluate_pixel[x].blue);
700 if (evaluate_image->matte == MagickFalse)
701 q->opacity=ClampToQuantum(evaluate_pixel[x].opacity);
702 else
703 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[x].opacity);
704 if (evaluate_image->colorspace == CMYKColorspace)
705 evaluate_indexes[x]=ClampToQuantum(evaluate_pixel[x].index);
706 q++;
707 }
708 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
709 status=MagickFalse;
710 if (images->progress_monitor != (MagickProgressMonitor) NULL)
711 {
712 MagickBooleanType
713 proceed;
714
715#if defined(MAGICKCORE_OPENMP_SUPPORT)
716 #pragma omp critical (MagickCore_EvaluateImages)
717#endif
718 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
719 evaluate_image->rows);
720 if (proceed == MagickFalse)
721 status=MagickFalse;
722 }
723 }
cristyd18ae7c2010-03-07 17:39:52 +0000724 evaluate_view=DestroyCacheView(evaluate_view);
725 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
726 random_info=DestroyRandomInfoThreadSet(random_info);
727 if (status == MagickFalse)
728 evaluate_image=DestroyImage(evaluate_image);
729 return(evaluate_image);
730}
731
cristy351842f2010-03-07 15:27:38 +0000732MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
733 const ChannelType channel,const MagickEvaluateOperator op,const double value,
734 ExceptionInfo *exception)
735{
cristy351842f2010-03-07 15:27:38 +0000736 CacheView
737 *image_view;
738
cristy351842f2010-03-07 15:27:38 +0000739 MagickBooleanType
740 status;
741
cristy5f959472010-05-27 22:19:46 +0000742 MagickOffsetType
743 progress;
744
cristy351842f2010-03-07 15:27:38 +0000745 RandomInfo
746 **restrict random_info;
747
cristy5f959472010-05-27 22:19:46 +0000748 ssize_t
749 y;
750
cristy351842f2010-03-07 15:27:38 +0000751 assert(image != (Image *) NULL);
752 assert(image->signature == MagickSignature);
753 if (image->debug != MagickFalse)
754 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
755 assert(exception != (ExceptionInfo *) NULL);
756 assert(exception->signature == MagickSignature);
757 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
758 {
759 InheritException(exception,&image->exception);
760 return(MagickFalse);
761 }
762 status=MagickTrue;
763 progress=0;
764 random_info=AcquireRandomInfoThreadSet();
765 image_view=AcquireCacheView(image);
766#if defined(MAGICKCORE_OPENMP_SUPPORT)
767 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
768#endif
cristybb503372010-05-27 20:51:26 +0000769 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000770 {
cristy5c9e6f22010-09-17 17:31:01 +0000771 const int
772 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000773
cristy351842f2010-03-07 15:27:38 +0000774 register IndexPacket
775 *restrict indexes;
776
cristy351842f2010-03-07 15:27:38 +0000777 register PixelPacket
778 *restrict q;
779
cristy5c9e6f22010-09-17 17:31:01 +0000780 register ssize_t
781 x;
782
cristy351842f2010-03-07 15:27:38 +0000783 if (status == MagickFalse)
784 continue;
785 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
786 if (q == (PixelPacket *) NULL)
787 {
788 status=MagickFalse;
789 continue;
790 }
791 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000792 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000793 {
794 if ((channel & RedChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000795 q->red=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->red,op,
796 value));
cristy351842f2010-03-07 15:27:38 +0000797 if ((channel & GreenChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000798 q->green=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->green,
799 op,value));
cristy351842f2010-03-07 15:27:38 +0000800 if ((channel & BlueChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000801 q->blue=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->blue,op,
802 value));
cristy351842f2010-03-07 15:27:38 +0000803 if ((channel & OpacityChannel) != 0)
804 {
805 if (image->matte == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000806 q->opacity=ClampToQuantum(ApplyEvaluateOperator(random_info[id],
807 q->opacity,op,value));
cristy351842f2010-03-07 15:27:38 +0000808 else
cristyd18ae7c2010-03-07 17:39:52 +0000809 q->opacity=ClampToQuantum(QuantumRange-ApplyEvaluateOperator(
810 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value));
cristy351842f2010-03-07 15:27:38 +0000811 }
812 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyd18ae7c2010-03-07 17:39:52 +0000813 indexes[x]=(IndexPacket) ClampToQuantum(ApplyEvaluateOperator(
814 random_info[id],indexes[x],op,value));
cristy351842f2010-03-07 15:27:38 +0000815 q++;
816 }
817 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
818 status=MagickFalse;
819 if (image->progress_monitor != (MagickProgressMonitor) NULL)
820 {
821 MagickBooleanType
822 proceed;
823
824#if defined(MAGICKCORE_OPENMP_SUPPORT)
825 #pragma omp critical (MagickCore_EvaluateImageChannel)
826#endif
827 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
828 if (proceed == MagickFalse)
829 status=MagickFalse;
830 }
831 }
832 image_view=DestroyCacheView(image_view);
833 random_info=DestroyRandomInfoThreadSet(random_info);
834 return(status);
835}
836
837/*
838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839% %
840% %
841% %
842% F u n c t i o n I m a g e %
843% %
844% %
845% %
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847%
848% FunctionImage() applies a value to the image with an arithmetic, relational,
849% or logical operator to an image. Use these operations to lighten or darken
850% an image, to increase or decrease contrast in an image, or to produce the
851% "negative" of an image.
852%
853% The format of the FunctionImageChannel method is:
854%
855% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000856% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000857% const double *parameters,ExceptionInfo *exception)
858% MagickBooleanType FunctionImageChannel(Image *image,
859% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000860% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000861% ExceptionInfo *exception)
862%
863% A description of each parameter follows:
864%
865% o image: the image.
866%
867% o channel: the channel.
868%
869% o function: A channel function.
870%
871% o parameters: one or more parameters.
872%
873% o exception: return any errors or warnings in this structure.
874%
875*/
876
877static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000878 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000879 ExceptionInfo *exception)
880{
881 MagickRealType
882 result;
883
cristybb503372010-05-27 20:51:26 +0000884 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000885 i;
886
887 (void) exception;
888 result=0.0;
889 switch (function)
890 {
891 case PolynomialFunction:
892 {
893 /*
894 * Polynomial
895 * Parameters: polynomial constants, highest to lowest order
896 * For example: c0*x^3 + c1*x^2 + c2*x + c3
897 */
898 result=0.0;
cristybb503372010-05-27 20:51:26 +0000899 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000900 result = result*QuantumScale*pixel + parameters[i];
901 result *= QuantumRange;
902 break;
903 }
904 case SinusoidFunction:
905 {
906 /* Sinusoid Function
907 * Parameters: Freq, Phase, Ampl, bias
908 */
909 double freq,phase,ampl,bias;
910 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
911 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
912 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
913 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
914 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
915 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
916 break;
917 }
918 case ArcsinFunction:
919 {
920 /* Arcsin Function (peged at range limits for invalid results)
921 * Parameters: Width, Center, Range, Bias
922 */
923 double width,range,center,bias;
924 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
925 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
926 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
927 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
928 result = 2.0/width*(QuantumScale*pixel - center);
929 if ( result <= -1.0 )
930 result = bias - range/2.0;
931 else if ( result >= 1.0 )
932 result = bias + range/2.0;
933 else
cristy7a07f9f2010-11-27 19:09:51 +0000934 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
cristy351842f2010-03-07 15:27:38 +0000935 result *= QuantumRange;
936 break;
937 }
938 case ArctanFunction:
939 {
940 /* Arctan Function
941 * Parameters: Slope, Center, Range, Bias
942 */
943 double slope,range,center,bias;
944 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
945 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
946 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
947 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000948 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000949 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
950 result) + bias ) );
951 break;
952 }
953 case UndefinedFunction:
954 break;
955 }
956 return(ClampToQuantum(result));
957}
958
959MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000960 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000961 const double *parameters,ExceptionInfo *exception)
962{
963 MagickBooleanType
964 status;
965
966 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
967 parameters,exception);
968 return(status);
969}
970
971MagickExport MagickBooleanType FunctionImageChannel(Image *image,
972 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000973 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000974 ExceptionInfo *exception)
975{
976#define FunctionImageTag "Function/Image "
977
978 CacheView
979 *image_view;
980
cristy351842f2010-03-07 15:27:38 +0000981 MagickBooleanType
982 status;
983
cristy5f959472010-05-27 22:19:46 +0000984 MagickOffsetType
985 progress;
986
987 ssize_t
988 y;
989
cristy351842f2010-03-07 15:27:38 +0000990 assert(image != (Image *) NULL);
991 assert(image->signature == MagickSignature);
992 if (image->debug != MagickFalse)
993 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
994 assert(exception != (ExceptionInfo *) NULL);
995 assert(exception->signature == MagickSignature);
996 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
997 {
998 InheritException(exception,&image->exception);
999 return(MagickFalse);
1000 }
1001 status=MagickTrue;
1002 progress=0;
1003 image_view=AcquireCacheView(image);
1004#if defined(MAGICKCORE_OPENMP_SUPPORT)
1005 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1006#endif
cristybb503372010-05-27 20:51:26 +00001007 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +00001008 {
1009 register IndexPacket
1010 *restrict indexes;
1011
cristybb503372010-05-27 20:51:26 +00001012 register ssize_t
cristy351842f2010-03-07 15:27:38 +00001013 x;
1014
1015 register PixelPacket
1016 *restrict q;
1017
1018 if (status == MagickFalse)
1019 continue;
1020 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1021 if (q == (PixelPacket *) NULL)
1022 {
1023 status=MagickFalse;
1024 continue;
1025 }
1026 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001027 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001028 {
1029 if ((channel & RedChannel) != 0)
1030 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
1031 exception);
1032 if ((channel & GreenChannel) != 0)
1033 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
1034 exception);
1035 if ((channel & BlueChannel) != 0)
1036 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
1037 exception);
1038 if ((channel & OpacityChannel) != 0)
1039 {
1040 if (image->matte == MagickFalse)
1041 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
1042 parameters,exception);
1043 else
1044 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum)
1045 GetAlphaPixelComponent(q),function,number_parameters,parameters,
1046 exception);
1047 }
1048 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
1049 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
1050 number_parameters,parameters,exception);
1051 q++;
1052 }
1053 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1054 status=MagickFalse;
1055 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1056 {
1057 MagickBooleanType
1058 proceed;
1059
1060#if defined(MAGICKCORE_OPENMP_SUPPORT)
1061 #pragma omp critical (MagickCore_FunctionImageChannel)
1062#endif
1063 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1064 if (proceed == MagickFalse)
1065 status=MagickFalse;
1066 }
1067 }
1068 image_view=DestroyCacheView(image_view);
1069 return(status);
1070}
1071
1072/*
1073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074% %
1075% %
1076% %
cristy3ed852e2009-09-05 21:47:34 +00001077+ G e t I m a g e C h a n n e l E x t r e m a %
1078% %
1079% %
1080% %
1081%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1082%
1083% GetImageChannelExtrema() returns the extrema of one or more image channels.
1084%
1085% The format of the GetImageChannelExtrema method is:
1086%
1087% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001088% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001089% ExceptionInfo *exception)
1090%
1091% A description of each parameter follows:
1092%
1093% o image: the image.
1094%
1095% o channel: the channel.
1096%
1097% o minima: the minimum value in the channel.
1098%
1099% o maxima: the maximum value in the channel.
1100%
1101% o exception: return any errors or warnings in this structure.
1102%
1103*/
1104
1105MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001106 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
1109}
1110
1111MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001112 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001113 ExceptionInfo *exception)
1114{
1115 double
1116 max,
1117 min;
1118
1119 MagickBooleanType
1120 status;
1121
1122 assert(image != (Image *) NULL);
1123 assert(image->signature == MagickSignature);
1124 if (image->debug != MagickFalse)
1125 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1126 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001127 *minima=(size_t) ceil(min-0.5);
1128 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001129 return(status);
1130}
1131
1132/*
1133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1134% %
1135% %
1136% %
1137% G e t I m a g e C h a n n e l M e a n %
1138% %
1139% %
1140% %
1141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1142%
1143% GetImageChannelMean() returns the mean and standard deviation of one or more
1144% image channels.
1145%
1146% The format of the GetImageChannelMean method is:
1147%
1148% MagickBooleanType GetImageChannelMean(const Image *image,
1149% const ChannelType channel,double *mean,double *standard_deviation,
1150% ExceptionInfo *exception)
1151%
1152% A description of each parameter follows:
1153%
1154% o image: the image.
1155%
1156% o channel: the channel.
1157%
1158% o mean: the average value in the channel.
1159%
1160% o standard_deviation: the standard deviation of the channel.
1161%
1162% o exception: return any errors or warnings in this structure.
1163%
1164*/
1165
1166MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1167 double *standard_deviation,ExceptionInfo *exception)
1168{
1169 MagickBooleanType
1170 status;
1171
1172 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
1173 exception);
1174 return(status);
1175}
1176
1177MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1178 const ChannelType channel,double *mean,double *standard_deviation,
1179 ExceptionInfo *exception)
1180{
cristyfd9dcd42010-08-08 18:07:02 +00001181 ChannelStatistics
1182 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001183
cristyfd9dcd42010-08-08 18:07:02 +00001184 size_t
1185 channels;
cristy3ed852e2009-09-05 21:47:34 +00001186
1187 assert(image != (Image *) NULL);
1188 assert(image->signature == MagickSignature);
1189 if (image->debug != MagickFalse)
1190 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001191 channel_statistics=GetImageChannelStatistics(image,exception);
1192 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001193 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001194 channels=0;
1195 channel_statistics[AllChannels].mean=0.0;
1196 channel_statistics[AllChannels].standard_deviation=0.0;
1197 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001198 {
cristyfd9dcd42010-08-08 18:07:02 +00001199 channel_statistics[AllChannels].mean+=
1200 channel_statistics[RedChannel].mean;
1201 channel_statistics[AllChannels].standard_deviation+=
1202 channel_statistics[RedChannel].variance-
1203 channel_statistics[RedChannel].mean*
1204 channel_statistics[RedChannel].mean;
1205 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001206 }
cristyfd9dcd42010-08-08 18:07:02 +00001207 if ((channel & GreenChannel) != 0)
1208 {
1209 channel_statistics[AllChannels].mean+=
1210 channel_statistics[GreenChannel].mean;
1211 channel_statistics[AllChannels].standard_deviation+=
1212 channel_statistics[GreenChannel].variance-
1213 channel_statistics[GreenChannel].mean*
1214 channel_statistics[GreenChannel].mean;
1215 channels++;
1216 }
1217 if ((channel & BlueChannel) != 0)
1218 {
1219 channel_statistics[AllChannels].mean+=
1220 channel_statistics[BlueChannel].mean;
1221 channel_statistics[AllChannels].standard_deviation+=
1222 channel_statistics[BlueChannel].variance-
1223 channel_statistics[BlueChannel].mean*
1224 channel_statistics[BlueChannel].mean;
1225 channels++;
1226 }
1227 if (((channel & OpacityChannel) != 0) &&
1228 (image->matte != MagickFalse))
1229 {
1230 channel_statistics[AllChannels].mean+=
1231 channel_statistics[OpacityChannel].mean;
1232 channel_statistics[AllChannels].standard_deviation+=
1233 channel_statistics[OpacityChannel].variance-
1234 channel_statistics[OpacityChannel].mean*
1235 channel_statistics[OpacityChannel].mean;
1236 channels++;
1237 }
1238 if (((channel & IndexChannel) != 0) &&
1239 (image->colorspace == CMYKColorspace))
1240 {
1241 channel_statistics[AllChannels].mean+=
1242 channel_statistics[BlackChannel].mean;
1243 channel_statistics[AllChannels].standard_deviation+=
1244 channel_statistics[BlackChannel].variance-
1245 channel_statistics[BlackChannel].mean*
1246 channel_statistics[BlackChannel].mean;
1247 channels++;
1248 }
1249 channel_statistics[AllChannels].mean/=channels;
1250 channel_statistics[AllChannels].standard_deviation=
1251 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
1252 *mean=channel_statistics[AllChannels].mean;
1253 *standard_deviation=channel_statistics[AllChannels].standard_deviation;
1254 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1255 channel_statistics);
1256 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001257}
1258
1259/*
1260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1261% %
1262% %
1263% %
1264% G e t I m a g e C h a n n e l K u r t o s i s %
1265% %
1266% %
1267% %
1268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269%
1270% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1271% image channels.
1272%
1273% The format of the GetImageChannelKurtosis method is:
1274%
1275% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1276% const ChannelType channel,double *kurtosis,double *skewness,
1277% ExceptionInfo *exception)
1278%
1279% A description of each parameter follows:
1280%
1281% o image: the image.
1282%
1283% o channel: the channel.
1284%
1285% o kurtosis: the kurtosis of the channel.
1286%
1287% o skewness: the skewness of the channel.
1288%
1289% o exception: return any errors or warnings in this structure.
1290%
1291*/
1292
1293MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1294 double *kurtosis,double *skewness,ExceptionInfo *exception)
1295{
1296 MagickBooleanType
1297 status;
1298
1299 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
1300 exception);
1301 return(status);
1302}
1303
1304MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1305 const ChannelType channel,double *kurtosis,double *skewness,
1306 ExceptionInfo *exception)
1307{
1308 double
1309 area,
1310 mean,
1311 standard_deviation,
1312 sum_squares,
1313 sum_cubes,
1314 sum_fourth_power;
1315
cristybb503372010-05-27 20:51:26 +00001316 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001317 y;
1318
1319 assert(image != (Image *) NULL);
1320 assert(image->signature == MagickSignature);
1321 if (image->debug != MagickFalse)
1322 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1323 *kurtosis=0.0;
1324 *skewness=0.0;
1325 area=0.0;
1326 mean=0.0;
1327 standard_deviation=0.0;
1328 sum_squares=0.0;
1329 sum_cubes=0.0;
1330 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001331 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001332 {
1333 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001334 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001335
1336 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001337 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001338
cristybb503372010-05-27 20:51:26 +00001339 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001340 x;
1341
1342 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1343 if (p == (const PixelPacket *) NULL)
1344 break;
1345 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001346 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001347 {
1348 if ((channel & RedChannel) != 0)
1349 {
cristyce70c172010-01-07 17:15:30 +00001350 mean+=GetRedPixelComponent(p);
1351 sum_squares+=(double) p->red*GetRedPixelComponent(p);
1352 sum_cubes+=(double) p->red*p->red*GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001353 sum_fourth_power+=(double) p->red*p->red*p->red*
1354 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001355 area++;
1356 }
1357 if ((channel & GreenChannel) != 0)
1358 {
cristyce70c172010-01-07 17:15:30 +00001359 mean+=GetGreenPixelComponent(p);
1360 sum_squares+=(double) p->green*GetGreenPixelComponent(p);
1361 sum_cubes+=(double) p->green*p->green*GetGreenPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001362 sum_fourth_power+=(double) p->green*p->green*p->green*
1363 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001364 area++;
1365 }
1366 if ((channel & BlueChannel) != 0)
1367 {
cristyce70c172010-01-07 17:15:30 +00001368 mean+=GetBluePixelComponent(p);
1369 sum_squares+=(double) p->blue*GetBluePixelComponent(p);
1370 sum_cubes+=(double) p->blue*p->blue*GetBluePixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001371 sum_fourth_power+=(double) p->blue*p->blue*p->blue*
1372 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001373 area++;
1374 }
1375 if ((channel & OpacityChannel) != 0)
1376 {
cristyce70c172010-01-07 17:15:30 +00001377 mean+=GetOpacityPixelComponent(p);
1378 sum_squares+=(double) p->opacity*GetOpacityPixelComponent(p);
1379 sum_cubes+=(double) p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001380 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
cristyce70c172010-01-07 17:15:30 +00001381 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001382 area++;
1383 }
1384 if (((channel & IndexChannel) != 0) &&
1385 (image->colorspace == CMYKColorspace))
1386 {
1387 mean+=indexes[x];
1388 sum_squares+=(double) indexes[x]*indexes[x];
1389 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
1390 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1391 indexes[x];
1392 area++;
1393 }
1394 p++;
1395 }
1396 }
cristybb503372010-05-27 20:51:26 +00001397 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001398 return(MagickFalse);
1399 if (area != 0.0)
1400 {
1401 mean/=area;
1402 sum_squares/=area;
1403 sum_cubes/=area;
1404 sum_fourth_power/=area;
1405 }
1406 standard_deviation=sqrt(sum_squares-(mean*mean));
1407 if (standard_deviation != 0.0)
1408 {
1409 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1410 3.0*mean*mean*mean*mean;
1411 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1412 standard_deviation;
1413 *kurtosis-=3.0;
1414 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1415 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1416 }
cristybb503372010-05-27 20:51:26 +00001417 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001418}
cristy46f08202010-01-10 04:04:21 +00001419
cristy3ed852e2009-09-05 21:47:34 +00001420/*
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422% %
1423% %
1424% %
1425% G e t I m a g e C h a n n e l R a n g e %
1426% %
1427% %
1428% %
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430%
1431% GetImageChannelRange() returns the range of one or more image channels.
1432%
1433% The format of the GetImageChannelRange method is:
1434%
1435% MagickBooleanType GetImageChannelRange(const Image *image,
1436% const ChannelType channel,double *minima,double *maxima,
1437% ExceptionInfo *exception)
1438%
1439% A description of each parameter follows:
1440%
1441% o image: the image.
1442%
1443% o channel: the channel.
1444%
1445% o minima: the minimum value in the channel.
1446%
1447% o maxima: the maximum value in the channel.
1448%
1449% o exception: return any errors or warnings in this structure.
1450%
1451*/
1452
1453MagickExport MagickBooleanType GetImageRange(const Image *image,
1454 double *minima,double *maxima,ExceptionInfo *exception)
1455{
1456 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1457}
1458
1459MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1460 const ChannelType channel,double *minima,double *maxima,
1461 ExceptionInfo *exception)
1462{
cristybb503372010-05-27 20:51:26 +00001463 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001464 y;
1465
1466 MagickPixelPacket
1467 pixel;
1468
1469 assert(image != (Image *) NULL);
1470 assert(image->signature == MagickSignature);
1471 if (image->debug != MagickFalse)
1472 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1473 *maxima=(-1.0E-37);
1474 *minima=1.0E+37;
1475 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001476 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001477 {
1478 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001479 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001480
1481 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001482 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001483
cristybb503372010-05-27 20:51:26 +00001484 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001485 x;
1486
1487 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1488 if (p == (const PixelPacket *) NULL)
1489 break;
1490 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001491 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001492 {
1493 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1494 if ((channel & RedChannel) != 0)
1495 {
1496 if (pixel.red < *minima)
1497 *minima=(double) pixel.red;
1498 if (pixel.red > *maxima)
1499 *maxima=(double) pixel.red;
1500 }
1501 if ((channel & GreenChannel) != 0)
1502 {
1503 if (pixel.green < *minima)
1504 *minima=(double) pixel.green;
1505 if (pixel.green > *maxima)
1506 *maxima=(double) pixel.green;
1507 }
1508 if ((channel & BlueChannel) != 0)
1509 {
1510 if (pixel.blue < *minima)
1511 *minima=(double) pixel.blue;
1512 if (pixel.blue > *maxima)
1513 *maxima=(double) pixel.blue;
1514 }
1515 if ((channel & OpacityChannel) != 0)
1516 {
1517 if (pixel.opacity < *minima)
1518 *minima=(double) pixel.opacity;
1519 if (pixel.opacity > *maxima)
1520 *maxima=(double) pixel.opacity;
1521 }
1522 if (((channel & IndexChannel) != 0) &&
1523 (image->colorspace == CMYKColorspace))
1524 {
1525 if ((double) indexes[x] < *minima)
1526 *minima=(double) indexes[x];
1527 if ((double) indexes[x] > *maxima)
1528 *maxima=(double) indexes[x];
1529 }
1530 p++;
1531 }
1532 }
cristybb503372010-05-27 20:51:26 +00001533 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001534}
1535
1536/*
1537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1538% %
1539% %
1540% %
1541% 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 %
1542% %
1543% %
1544% %
1545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1546%
1547% GetImageChannelStatistics() returns statistics for each channel in the
1548% image. The statistics include the channel depth, its minima, maxima, mean,
1549% standard deviation, kurtosis and skewness. You can access the red channel
1550% mean, for example, like this:
1551%
1552% channel_statistics=GetImageChannelStatistics(image,excepton);
1553% red_mean=channel_statistics[RedChannel].mean;
1554%
1555% Use MagickRelinquishMemory() to free the statistics buffer.
1556%
1557% The format of the GetImageChannelStatistics method is:
1558%
1559% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1560% ExceptionInfo *exception)
1561%
1562% A description of each parameter follows:
1563%
1564% o image: the image.
1565%
1566% o exception: return any errors or warnings in this structure.
1567%
1568*/
cristy3ed852e2009-09-05 21:47:34 +00001569MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1570 ExceptionInfo *exception)
1571{
1572 ChannelStatistics
1573 *channel_statistics;
1574
1575 double
cristyfd9dcd42010-08-08 18:07:02 +00001576 area;
cristy3ed852e2009-09-05 21:47:34 +00001577
cristybb503372010-05-27 20:51:26 +00001578 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001579 y;
1580
1581 MagickStatusType
1582 status;
1583
1584 QuantumAny
1585 range;
1586
cristybb503372010-05-27 20:51:26 +00001587 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001588 i;
1589
1590 size_t
1591 length;
1592
cristybb503372010-05-27 20:51:26 +00001593 size_t
cristy3ed852e2009-09-05 21:47:34 +00001594 channels,
1595 depth;
1596
1597 assert(image != (Image *) NULL);
1598 assert(image->signature == MagickSignature);
1599 if (image->debug != MagickFalse)
1600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1601 length=AllChannels+1UL;
1602 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1603 sizeof(*channel_statistics));
1604 if (channel_statistics == (ChannelStatistics *) NULL)
1605 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1606 (void) ResetMagickMemory(channel_statistics,0,length*
1607 sizeof(*channel_statistics));
1608 for (i=0; i <= AllChannels; i++)
1609 {
1610 channel_statistics[i].depth=1;
1611 channel_statistics[i].maxima=(-1.0E-37);
1612 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001613 }
cristybb503372010-05-27 20:51:26 +00001614 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001615 {
1616 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001617 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001618
1619 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001620 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001621
cristybb503372010-05-27 20:51:26 +00001622 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001623 x;
1624
1625 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1626 if (p == (const PixelPacket *) NULL)
1627 break;
1628 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001629 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001630 {
1631 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1632 {
1633 depth=channel_statistics[RedChannel].depth;
1634 range=GetQuantumRange(depth);
1635 status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
1636 range) ? MagickTrue : MagickFalse;
1637 if (status != MagickFalse)
1638 {
1639 channel_statistics[RedChannel].depth++;
1640 continue;
1641 }
1642 }
1643 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1644 {
1645 depth=channel_statistics[GreenChannel].depth;
1646 range=GetQuantumRange(depth);
1647 status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
1648 range),range) ? MagickTrue : MagickFalse;
1649 if (status != MagickFalse)
1650 {
1651 channel_statistics[GreenChannel].depth++;
1652 continue;
1653 }
1654 }
1655 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1656 {
1657 depth=channel_statistics[BlueChannel].depth;
1658 range=GetQuantumRange(depth);
1659 status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
1660 range),range) ? MagickTrue : MagickFalse;
1661 if (status != MagickFalse)
1662 {
1663 channel_statistics[BlueChannel].depth++;
1664 continue;
1665 }
1666 }
1667 if (image->matte != MagickFalse)
1668 {
1669 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1670 {
1671 depth=channel_statistics[OpacityChannel].depth;
1672 range=GetQuantumRange(depth);
1673 status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
1674 p->opacity,range),range) ? MagickTrue : MagickFalse;
1675 if (status != MagickFalse)
1676 {
1677 channel_statistics[OpacityChannel].depth++;
1678 continue;
1679 }
1680 }
1681 }
1682 if (image->colorspace == CMYKColorspace)
1683 {
1684 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1685 {
1686 depth=channel_statistics[BlackChannel].depth;
1687 range=GetQuantumRange(depth);
1688 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1689 indexes[x],range),range) ? MagickTrue : MagickFalse;
1690 if (status != MagickFalse)
1691 {
1692 channel_statistics[BlackChannel].depth++;
1693 continue;
1694 }
1695 }
1696 }
1697 if ((double) p->red < channel_statistics[RedChannel].minima)
cristyce70c172010-01-07 17:15:30 +00001698 channel_statistics[RedChannel].minima=(double) GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001699 if ((double) p->red > channel_statistics[RedChannel].maxima)
cristyce70c172010-01-07 17:15:30 +00001700 channel_statistics[RedChannel].maxima=(double) GetRedPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001701 channel_statistics[RedChannel].sum+=GetRedPixelComponent(p);
1702 channel_statistics[RedChannel].sum_squared+=(double) p->red*
cristy46f08202010-01-10 04:04:21 +00001703 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001704 channel_statistics[RedChannel].sum_cubed+=(double) p->red*p->red*
cristy46f08202010-01-10 04:04:21 +00001705 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001706 channel_statistics[RedChannel].sum_fourth_power+=(double) p->red*p->red*
1707 p->red*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001708 if ((double) p->green < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001709 channel_statistics[GreenChannel].minima=(double)
1710 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001711 if ((double) p->green > channel_statistics[GreenChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001712 channel_statistics[GreenChannel].maxima=(double)
1713 GetGreenPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001714 channel_statistics[GreenChannel].sum+=GetGreenPixelComponent(p);
1715 channel_statistics[GreenChannel].sum_squared+=(double) p->green*
cristyce70c172010-01-07 17:15:30 +00001716 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001717 channel_statistics[GreenChannel].sum_cubed+=(double) p->green*p->green*
cristyce70c172010-01-07 17:15:30 +00001718 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001719 channel_statistics[GreenChannel].sum_fourth_power+=(double) p->green*
1720 p->green*p->green*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001721 if ((double) p->blue < channel_statistics[BlueChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001722 channel_statistics[BlueChannel].minima=(double)
1723 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001724 if ((double) p->blue > channel_statistics[BlueChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001725 channel_statistics[BlueChannel].maxima=(double)
1726 GetBluePixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001727 channel_statistics[BlueChannel].sum+=GetBluePixelComponent(p);
1728 channel_statistics[BlueChannel].sum_squared+=(double) p->blue*
cristyce70c172010-01-07 17:15:30 +00001729 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001730 channel_statistics[BlueChannel].sum_cubed+=(double) p->blue*p->blue*
cristyce70c172010-01-07 17:15:30 +00001731 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001732 channel_statistics[BlueChannel].sum_fourth_power+=(double) p->blue*
1733 p->blue*p->blue*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001734 if (image->matte != MagickFalse)
1735 {
1736 if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001737 channel_statistics[OpacityChannel].minima=(double)
1738 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001739 if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001740 channel_statistics[OpacityChannel].maxima=(double)
1741 GetOpacityPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001742 channel_statistics[OpacityChannel].sum+=GetOpacityPixelComponent(p);
1743 channel_statistics[OpacityChannel].sum_squared+=(double)
cristyce70c172010-01-07 17:15:30 +00001744 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001745 channel_statistics[OpacityChannel].sum_cubed+=(double) p->opacity*
cristyce70c172010-01-07 17:15:30 +00001746 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001747 channel_statistics[OpacityChannel].sum_fourth_power+=(double)
1748 p->opacity*p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001749 }
1750 if (image->colorspace == CMYKColorspace)
1751 {
1752 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1753 channel_statistics[BlackChannel].minima=(double) indexes[x];
1754 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1755 channel_statistics[BlackChannel].maxima=(double) indexes[x];
cristyfd9dcd42010-08-08 18:07:02 +00001756 channel_statistics[BlackChannel].sum+=indexes[x];
1757 channel_statistics[BlackChannel].sum_squared+=(double)
cristy3ed852e2009-09-05 21:47:34 +00001758 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001759 channel_statistics[BlackChannel].sum_cubed+=(double) indexes[x]*
cristy3ed852e2009-09-05 21:47:34 +00001760 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001761 channel_statistics[BlackChannel].sum_fourth_power+=(double)
1762 indexes[x]*indexes[x]*indexes[x]*indexes[x];
cristy3ed852e2009-09-05 21:47:34 +00001763 }
1764 x++;
1765 p++;
1766 }
1767 }
1768 area=(double) image->columns*image->rows;
1769 for (i=0; i < AllChannels; i++)
1770 {
cristyfd9dcd42010-08-08 18:07:02 +00001771 channel_statistics[i].sum/=area;
1772 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001773 channel_statistics[i].sum_cubed/=area;
1774 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001775 channel_statistics[i].mean=channel_statistics[i].sum;
1776 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1777 channel_statistics[i].standard_deviation=sqrt(
1778 channel_statistics[i].variance-(channel_statistics[i].mean*
1779 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001780 }
1781 for (i=0; i < AllChannels; i++)
1782 {
cristybb503372010-05-27 20:51:26 +00001783 channel_statistics[AllChannels].depth=(size_t) MagickMax((double)
cristy3ed852e2009-09-05 21:47:34 +00001784 channel_statistics[AllChannels].depth,(double)
1785 channel_statistics[i].depth);
1786 channel_statistics[AllChannels].minima=MagickMin(
1787 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1788 channel_statistics[AllChannels].maxima=MagickMax(
1789 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
cristyfd9dcd42010-08-08 18:07:02 +00001790 channel_statistics[AllChannels].sum+=channel_statistics[i].sum;
1791 channel_statistics[AllChannels].sum_squared+=
1792 channel_statistics[i].sum_squared;
cristya8178ed2010-08-10 17:31:59 +00001793 channel_statistics[AllChannels].sum_cubed+=channel_statistics[i].sum_cubed;
1794 channel_statistics[AllChannels].sum_fourth_power+=
1795 channel_statistics[i].sum_fourth_power;
cristy3ed852e2009-09-05 21:47:34 +00001796 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
cristyfd9dcd42010-08-08 18:07:02 +00001797 channel_statistics[AllChannels].variance+=channel_statistics[i].variance-
1798 channel_statistics[i].mean*channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001799 channel_statistics[AllChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001800 channel_statistics[i].variance-channel_statistics[i].mean*
1801 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001802 }
cristycf584452010-08-08 02:26:04 +00001803 channels=3;
1804 if (image->matte != MagickFalse)
1805 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001806 if (image->colorspace == CMYKColorspace)
1807 channels++;
cristyfd9dcd42010-08-08 18:07:02 +00001808 channel_statistics[AllChannels].sum/=channels;
1809 channel_statistics[AllChannels].sum_squared/=channels;
cristya8178ed2010-08-10 17:31:59 +00001810 channel_statistics[AllChannels].sum_cubed/=channels;
1811 channel_statistics[AllChannels].sum_fourth_power/=channels;
cristy3ed852e2009-09-05 21:47:34 +00001812 channel_statistics[AllChannels].mean/=channels;
cristyfd9dcd42010-08-08 18:07:02 +00001813 channel_statistics[AllChannels].variance/=channels;
1814 channel_statistics[AllChannels].standard_deviation=
1815 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
cristy3ed852e2009-09-05 21:47:34 +00001816 channel_statistics[AllChannels].kurtosis/=channels;
1817 channel_statistics[AllChannels].skewness/=channels;
1818 for (i=0; i <= AllChannels; i++)
1819 {
cristy3ed852e2009-09-05 21:47:34 +00001820 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001821 continue;
1822 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1823 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1824 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1825 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1826 channel_statistics[i].standard_deviation*
1827 channel_statistics[i].standard_deviation);
1828 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1829 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1830 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1831 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1832 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1833 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1834 channel_statistics[i].standard_deviation*
1835 channel_statistics[i].standard_deviation*
1836 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001837 }
1838 return(channel_statistics);
1839}