blob: db53e3deefb6de38da2ba9262f976134f71bcbae [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% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/property.h"
45#include "magick/animate.h"
46#include "magick/blob.h"
47#include "magick/blob-private.h"
48#include "magick/cache.h"
49#include "magick/cache-private.h"
50#include "magick/cache-view.h"
51#include "magick/client.h"
52#include "magick/color.h"
53#include "magick/color-private.h"
54#include "magick/colorspace.h"
55#include "magick/colorspace-private.h"
56#include "magick/composite.h"
57#include "magick/composite-private.h"
58#include "magick/compress.h"
59#include "magick/constitute.h"
60#include "magick/deprecate.h"
61#include "magick/display.h"
62#include "magick/draw.h"
63#include "magick/enhance.h"
64#include "magick/exception.h"
65#include "magick/exception-private.h"
66#include "magick/gem.h"
67#include "magick/geometry.h"
68#include "magick/list.h"
69#include "magick/image-private.h"
70#include "magick/magic.h"
71#include "magick/magick.h"
72#include "magick/memory_.h"
73#include "magick/module.h"
74#include "magick/monitor.h"
cristyacd5a392009-09-18 00:04:22 +000075#include "magick/monitor-private.h"
cristy3ed852e2009-09-05 21:47:34 +000076#include "magick/option.h"
77#include "magick/paint.h"
78#include "magick/pixel-private.h"
79#include "magick/profile.h"
80#include "magick/quantize.h"
81#include "magick/random_.h"
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
148static MagickPixelPacket **AcquirePixelThreadSet(const Image *image)
149{
cristybb503372010-05-27 20:51:26 +0000150 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000151 i,
152 j;
153
154 MagickPixelPacket
155 **pixels;
156
cristybb503372010-05-27 20:51:26 +0000157 size_t
cristy316d5172009-09-17 19:31:25 +0000158 number_threads;
159
160 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +0000161 pixels=(MagickPixelPacket **) AcquireQuantumMemory(number_threads,
cristy316d5172009-09-17 19:31:25 +0000162 sizeof(*pixels));
163 if (pixels == (MagickPixelPacket **) NULL)
164 return((MagickPixelPacket **) NULL);
165 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000166 for (i=0; i < (ssize_t) number_threads; i++)
cristy316d5172009-09-17 19:31:25 +0000167 {
168 pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(image->columns,
169 sizeof(**pixels));
170 if (pixels[i] == (MagickPixelPacket *) NULL)
171 return(DestroyPixelThreadSet(pixels));
cristybb503372010-05-27 20:51:26 +0000172 for (j=0; j < (ssize_t) image->columns; j++)
cristy316d5172009-09-17 19:31:25 +0000173 GetMagickPixelPacket(image,&pixels[i][j]);
174 }
175 return(pixels);
176}
177
cristy351842f2010-03-07 15:27:38 +0000178static inline double MagickMax(const double x,const double y)
179{
180 if (x > y)
181 return(x);
182 return(y);
183}
184
185static inline double MagickMin(const double x,const double y)
186{
187 if (x < y)
188 return(x);
189 return(y);
190}
191
cristyd18ae7c2010-03-07 17:39:52 +0000192static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
193 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000194{
195 MagickRealType
196 result;
197
198 result=0.0;
199 switch (op)
200 {
201 case UndefinedEvaluateOperator:
202 break;
cristy33aaed22010-08-11 18:10:50 +0000203 case AbsEvaluateOperator:
204 {
205 result=(MagickRealType) fabs((double) (pixel+value));
206 break;
207 }
cristy351842f2010-03-07 15:27:38 +0000208 case AddEvaluateOperator:
209 {
210 result=(MagickRealType) (pixel+value);
211 break;
212 }
213 case AddModulusEvaluateOperator:
214 {
215 /*
216 This returns a 'floored modulus' of the addition which is a
217 positive result. It differs from % or fmod() which returns a
218 'truncated modulus' result, where floor() is replaced by trunc()
219 and could return a negative result (which is clipped).
220 */
221 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000222 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000223 break;
224 }
225 case AndEvaluateOperator:
226 {
cristy9fe8cd72010-10-19 01:24:07 +0000227 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000228 break;
229 }
230 case CosineEvaluateOperator:
231 {
232 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
233 QuantumScale*pixel*value))+0.5));
234 break;
235 }
236 case DivideEvaluateOperator:
237 {
238 result=pixel/(value == 0.0 ? 1.0 : value);
239 break;
240 }
cristy9fe8cd72010-10-19 01:24:07 +0000241 case ExponentialEvaluateOperator:
242 {
243 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
244 pixel)));
245 break;
246 }
cristy351842f2010-03-07 15:27:38 +0000247 case GaussianNoiseEvaluateOperator:
248 {
249 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
250 GaussianNoise,value);
251 break;
252 }
253 case ImpulseNoiseEvaluateOperator:
254 {
255 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
256 ImpulseNoise,value);
257 break;
258 }
259 case LaplacianNoiseEvaluateOperator:
260 {
261 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
262 LaplacianNoise,value);
263 break;
264 }
265 case LeftShiftEvaluateOperator:
266 {
cristy9fe8cd72010-10-19 01:24:07 +0000267 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000268 break;
269 }
270 case LogEvaluateOperator:
271 {
272 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
273 pixel+1.0))/log((double) (value+1.0)));
274 break;
275 }
276 case MaxEvaluateOperator:
277 {
278 result=(MagickRealType) MagickMax((double) pixel,value);
279 break;
280 }
281 case MeanEvaluateOperator:
282 {
cristy125a5a32010-05-07 13:30:52 +0000283 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000284 break;
285 }
286 case MinEvaluateOperator:
287 {
288 result=(MagickRealType) MagickMin((double) pixel,value);
289 break;
290 }
291 case MultiplicativeNoiseEvaluateOperator:
292 {
293 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
294 MultiplicativeGaussianNoise,value);
295 break;
296 }
297 case MultiplyEvaluateOperator:
298 {
299 result=(MagickRealType) (value*pixel);
300 break;
301 }
302 case OrEvaluateOperator:
303 {
cristy9fe8cd72010-10-19 01:24:07 +0000304 result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000305 break;
306 }
307 case PoissonNoiseEvaluateOperator:
308 {
309 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
310 PoissonNoise,value);
311 break;
312 }
313 case PowEvaluateOperator:
314 {
315 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
316 (double) value));
317 break;
318 }
319 case RightShiftEvaluateOperator:
320 {
cristy9fe8cd72010-10-19 01:24:07 +0000321 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000322 break;
323 }
324 case SetEvaluateOperator:
325 {
326 result=value;
327 break;
328 }
329 case SineEvaluateOperator:
330 {
331 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
332 QuantumScale*pixel*value))+0.5));
333 break;
334 }
335 case SubtractEvaluateOperator:
336 {
337 result=(MagickRealType) (pixel-value);
338 break;
339 }
340 case ThresholdEvaluateOperator:
341 {
342 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
343 QuantumRange);
344 break;
345 }
346 case ThresholdBlackEvaluateOperator:
347 {
348 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
349 break;
350 }
351 case ThresholdWhiteEvaluateOperator:
352 {
353 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
354 pixel);
355 break;
356 }
357 case UniformNoiseEvaluateOperator:
358 {
359 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
360 UniformNoise,value);
361 break;
362 }
363 case XorEvaluateOperator:
364 {
cristy9fe8cd72010-10-19 01:24:07 +0000365 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000366 break;
367 }
368 }
cristyd18ae7c2010-03-07 17:39:52 +0000369 return(result);
cristy351842f2010-03-07 15:27:38 +0000370}
371
372MagickExport MagickBooleanType EvaluateImage(Image *image,
373 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
374{
375 MagickBooleanType
376 status;
377
378 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
379 return(status);
380}
381
cristyd18ae7c2010-03-07 17:39:52 +0000382MagickExport Image *EvaluateImages(const Image *images,
383 const MagickEvaluateOperator op,ExceptionInfo *exception)
384{
385#define EvaluateImageTag "Evaluate/Image"
386
387 CacheView
388 *evaluate_view;
389
390 const Image
391 *next;
392
393 Image
394 *evaluate_image;
395
cristyd18ae7c2010-03-07 17:39:52 +0000396 MagickBooleanType
397 status;
398
cristy5f959472010-05-27 22:19:46 +0000399 MagickOffsetType
400 progress;
401
cristyd18ae7c2010-03-07 17:39:52 +0000402 MagickPixelPacket
403 **restrict evaluate_pixels,
404 zero;
405
406 RandomInfo
407 **restrict random_info;
408
cristybb503372010-05-27 20:51:26 +0000409 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000410 number_images;
411
cristy5f959472010-05-27 22:19:46 +0000412 ssize_t
413 y;
414
cristyd18ae7c2010-03-07 17:39:52 +0000415 /*
416 Ensure the image are the same size.
417 */
418 assert(images != (Image *) NULL);
419 assert(images->signature == MagickSignature);
420 if (images->debug != MagickFalse)
421 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
422 assert(exception != (ExceptionInfo *) NULL);
423 assert(exception->signature == MagickSignature);
424 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
425 if ((next->columns != images->columns) || (next->rows != images->rows))
426 {
427 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
428 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
429 return((Image *) NULL);
430 }
431 /*
432 Initialize evaluate next attributes.
433 */
434 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
435 exception);
436 if (evaluate_image == (Image *) NULL)
437 return((Image *) NULL);
438 if (SetImageStorageClass(evaluate_image,DirectClass) == MagickFalse)
439 {
440 InheritException(exception,&evaluate_image->exception);
441 evaluate_image=DestroyImage(evaluate_image);
442 return((Image *) NULL);
443 }
444 evaluate_pixels=AcquirePixelThreadSet(images);
445 if (evaluate_pixels == (MagickPixelPacket **) NULL)
446 {
447 evaluate_image=DestroyImage(evaluate_image);
448 (void) ThrowMagickException(exception,GetMagickModule(),
449 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
450 return((Image *) NULL);
451 }
452 /*
453 Evaluate image pixels.
454 */
455 status=MagickTrue;
456 progress=0;
457 GetMagickPixelPacket(images,&zero);
458 random_info=AcquireRandomInfoThreadSet();
459 number_images=GetImageListLength(images);
460 evaluate_view=AcquireCacheView(evaluate_image);
461#if defined(MAGICKCORE_OPENMP_SUPPORT)
462 #pragma omp parallel for schedule(dynamic) shared(progress,status)
463#endif
cristybb503372010-05-27 20:51:26 +0000464 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000465 {
466 CacheView
467 *image_view;
468
469 const Image
470 *next;
471
cristy5c9e6f22010-09-17 17:31:01 +0000472 const int
473 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000474
cristyd18ae7c2010-03-07 17:39:52 +0000475 MagickPixelPacket
476 pixel;
477
478 register IndexPacket
479 *restrict evaluate_indexes;
480
cristybb503372010-05-27 20:51:26 +0000481 register ssize_t
cristyd18ae7c2010-03-07 17:39:52 +0000482 i,
cristyd18ae7c2010-03-07 17:39:52 +0000483 x;
484
485 register MagickPixelPacket
486 *evaluate_pixel;
487
488 register PixelPacket
489 *restrict q;
490
491 if (status == MagickFalse)
492 continue;
493 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,1,
494 exception);
495 if (q == (PixelPacket *) NULL)
496 {
497 status=MagickFalse;
498 continue;
499 }
500 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
501 pixel=zero;
cristyd18ae7c2010-03-07 17:39:52 +0000502 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000503 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000504 evaluate_pixel[x]=zero;
505 next=images;
cristybb503372010-05-27 20:51:26 +0000506 for (i=0; i < (ssize_t) number_images; i++)
cristyd18ae7c2010-03-07 17:39:52 +0000507 {
508 register const IndexPacket
509 *indexes;
510
511 register const PixelPacket
512 *p;
513
514 image_view=AcquireCacheView(next);
515 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
516 if (p == (const PixelPacket *) NULL)
517 {
518 image_view=DestroyCacheView(image_view);
519 break;
520 }
521 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000522 for (x=0; x < (ssize_t) next->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000523 {
524 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],p->red,
cristy4a747d12010-03-08 20:00:33 +0000525 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
cristyd18ae7c2010-03-07 17:39:52 +0000526 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],p->green,
cristy4a747d12010-03-08 20:00:33 +0000527 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].green);
cristyd18ae7c2010-03-07 17:39:52 +0000528 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],p->blue,
cristy4a747d12010-03-08 20:00:33 +0000529 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].blue);
cristyd18ae7c2010-03-07 17:39:52 +0000530 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000531 p->opacity,i == 0 ? AddEvaluateOperator : op,
532 evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000533 if (evaluate_image->colorspace == CMYKColorspace)
534 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000535 indexes[x],i == 0 ? AddEvaluateOperator : op,
536 evaluate_pixel[x].index);
cristyd18ae7c2010-03-07 17:39:52 +0000537 p++;
538 }
539 image_view=DestroyCacheView(image_view);
540 next=GetNextImageInList(next);
541 }
cristy125a5a32010-05-07 13:30:52 +0000542 if (op == MeanEvaluateOperator)
cristybb503372010-05-27 20:51:26 +0000543 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000544 {
545 evaluate_pixel[x].red/=number_images;
546 evaluate_pixel[x].green/=number_images;
547 evaluate_pixel[x].blue/=number_images;
548 evaluate_pixel[x].opacity/=number_images;
549 evaluate_pixel[x].index/=number_images;
550 }
cristybb503372010-05-27 20:51:26 +0000551 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000552 {
553 q->red=ClampToQuantum(evaluate_pixel[x].red);
554 q->green=ClampToQuantum(evaluate_pixel[x].green);
555 q->blue=ClampToQuantum(evaluate_pixel[x].blue);
cristyc02484c2010-03-08 00:14:58 +0000556 if (evaluate_image->matte == MagickFalse)
557 q->opacity=ClampToQuantum(evaluate_pixel[x].opacity);
558 else
559 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000560 if (evaluate_image->colorspace == CMYKColorspace)
561 evaluate_indexes[x]=ClampToQuantum(evaluate_pixel[x].index);
562 q++;
563 }
564 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
565 status=MagickFalse;
566 if (images->progress_monitor != (MagickProgressMonitor) NULL)
567 {
568 MagickBooleanType
569 proceed;
570
571#if defined(MAGICKCORE_OPENMP_SUPPORT)
572 #pragma omp critical (MagickCore_EvaluateImages)
573#endif
574 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
575 evaluate_image->rows);
576 if (proceed == MagickFalse)
577 status=MagickFalse;
578 }
579 }
580 evaluate_view=DestroyCacheView(evaluate_view);
581 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
582 random_info=DestroyRandomInfoThreadSet(random_info);
583 if (status == MagickFalse)
584 evaluate_image=DestroyImage(evaluate_image);
585 return(evaluate_image);
586}
587
cristy351842f2010-03-07 15:27:38 +0000588MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
589 const ChannelType channel,const MagickEvaluateOperator op,const double value,
590 ExceptionInfo *exception)
591{
cristy351842f2010-03-07 15:27:38 +0000592 CacheView
593 *image_view;
594
cristy351842f2010-03-07 15:27:38 +0000595 MagickBooleanType
596 status;
597
cristy5f959472010-05-27 22:19:46 +0000598 MagickOffsetType
599 progress;
600
cristy351842f2010-03-07 15:27:38 +0000601 RandomInfo
602 **restrict random_info;
603
cristy5f959472010-05-27 22:19:46 +0000604 ssize_t
605 y;
606
cristy351842f2010-03-07 15:27:38 +0000607 assert(image != (Image *) NULL);
608 assert(image->signature == MagickSignature);
609 if (image->debug != MagickFalse)
610 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
611 assert(exception != (ExceptionInfo *) NULL);
612 assert(exception->signature == MagickSignature);
613 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
614 {
615 InheritException(exception,&image->exception);
616 return(MagickFalse);
617 }
618 status=MagickTrue;
619 progress=0;
620 random_info=AcquireRandomInfoThreadSet();
621 image_view=AcquireCacheView(image);
622#if defined(MAGICKCORE_OPENMP_SUPPORT)
623 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
624#endif
cristybb503372010-05-27 20:51:26 +0000625 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000626 {
cristy5c9e6f22010-09-17 17:31:01 +0000627 const int
628 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000629
cristy351842f2010-03-07 15:27:38 +0000630 register IndexPacket
631 *restrict indexes;
632
cristy351842f2010-03-07 15:27:38 +0000633 register PixelPacket
634 *restrict q;
635
cristy5c9e6f22010-09-17 17:31:01 +0000636 register ssize_t
637 x;
638
cristy351842f2010-03-07 15:27:38 +0000639 if (status == MagickFalse)
640 continue;
641 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
642 if (q == (PixelPacket *) NULL)
643 {
644 status=MagickFalse;
645 continue;
646 }
647 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000648 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000649 {
650 if ((channel & RedChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000651 q->red=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->red,op,
652 value));
cristy351842f2010-03-07 15:27:38 +0000653 if ((channel & GreenChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000654 q->green=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->green,
655 op,value));
cristy351842f2010-03-07 15:27:38 +0000656 if ((channel & BlueChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000657 q->blue=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->blue,op,
658 value));
cristy351842f2010-03-07 15:27:38 +0000659 if ((channel & OpacityChannel) != 0)
660 {
661 if (image->matte == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000662 q->opacity=ClampToQuantum(ApplyEvaluateOperator(random_info[id],
663 q->opacity,op,value));
cristy351842f2010-03-07 15:27:38 +0000664 else
cristyd18ae7c2010-03-07 17:39:52 +0000665 q->opacity=ClampToQuantum(QuantumRange-ApplyEvaluateOperator(
666 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value));
cristy351842f2010-03-07 15:27:38 +0000667 }
668 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyd18ae7c2010-03-07 17:39:52 +0000669 indexes[x]=(IndexPacket) ClampToQuantum(ApplyEvaluateOperator(
670 random_info[id],indexes[x],op,value));
cristy351842f2010-03-07 15:27:38 +0000671 q++;
672 }
673 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
674 status=MagickFalse;
675 if (image->progress_monitor != (MagickProgressMonitor) NULL)
676 {
677 MagickBooleanType
678 proceed;
679
680#if defined(MAGICKCORE_OPENMP_SUPPORT)
681 #pragma omp critical (MagickCore_EvaluateImageChannel)
682#endif
683 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
684 if (proceed == MagickFalse)
685 status=MagickFalse;
686 }
687 }
688 image_view=DestroyCacheView(image_view);
689 random_info=DestroyRandomInfoThreadSet(random_info);
690 return(status);
691}
692
693/*
694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695% %
696% %
697% %
698% F u n c t i o n I m a g e %
699% %
700% %
701% %
702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703%
704% FunctionImage() applies a value to the image with an arithmetic, relational,
705% or logical operator to an image. Use these operations to lighten or darken
706% an image, to increase or decrease contrast in an image, or to produce the
707% "negative" of an image.
708%
709% The format of the FunctionImageChannel method is:
710%
711% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000712% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000713% const double *parameters,ExceptionInfo *exception)
714% MagickBooleanType FunctionImageChannel(Image *image,
715% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000716% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000717% ExceptionInfo *exception)
718%
719% A description of each parameter follows:
720%
721% o image: the image.
722%
723% o channel: the channel.
724%
725% o function: A channel function.
726%
727% o parameters: one or more parameters.
728%
729% o exception: return any errors or warnings in this structure.
730%
731*/
732
733static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000734 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000735 ExceptionInfo *exception)
736{
737 MagickRealType
738 result;
739
cristybb503372010-05-27 20:51:26 +0000740 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000741 i;
742
743 (void) exception;
744 result=0.0;
745 switch (function)
746 {
747 case PolynomialFunction:
748 {
749 /*
750 * Polynomial
751 * Parameters: polynomial constants, highest to lowest order
752 * For example: c0*x^3 + c1*x^2 + c2*x + c3
753 */
754 result=0.0;
cristybb503372010-05-27 20:51:26 +0000755 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000756 result = result*QuantumScale*pixel + parameters[i];
757 result *= QuantumRange;
758 break;
759 }
760 case SinusoidFunction:
761 {
762 /* Sinusoid Function
763 * Parameters: Freq, Phase, Ampl, bias
764 */
765 double freq,phase,ampl,bias;
766 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
767 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
768 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
769 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
770 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
771 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
772 break;
773 }
774 case ArcsinFunction:
775 {
776 /* Arcsin Function (peged at range limits for invalid results)
777 * Parameters: Width, Center, Range, Bias
778 */
779 double width,range,center,bias;
780 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
781 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
782 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
783 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
784 result = 2.0/width*(QuantumScale*pixel - center);
785 if ( result <= -1.0 )
786 result = bias - range/2.0;
787 else if ( result >= 1.0 )
788 result = bias + range/2.0;
789 else
790 result=range/MagickPI*asin((double)result) + bias;
791 result *= QuantumRange;
792 break;
793 }
794 case ArctanFunction:
795 {
796 /* Arctan Function
797 * Parameters: Slope, Center, Range, Bias
798 */
799 double slope,range,center,bias;
800 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
801 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
802 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
803 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
804 result = MagickPI*slope*(QuantumScale*pixel - center);
805 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
806 result) + bias ) );
807 break;
808 }
809 case UndefinedFunction:
810 break;
811 }
812 return(ClampToQuantum(result));
813}
814
815MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000816 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000817 const double *parameters,ExceptionInfo *exception)
818{
819 MagickBooleanType
820 status;
821
822 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
823 parameters,exception);
824 return(status);
825}
826
827MagickExport MagickBooleanType FunctionImageChannel(Image *image,
828 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000829 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000830 ExceptionInfo *exception)
831{
832#define FunctionImageTag "Function/Image "
833
834 CacheView
835 *image_view;
836
cristy351842f2010-03-07 15:27:38 +0000837 MagickBooleanType
838 status;
839
cristy5f959472010-05-27 22:19:46 +0000840 MagickOffsetType
841 progress;
842
843 ssize_t
844 y;
845
cristy351842f2010-03-07 15:27:38 +0000846 assert(image != (Image *) NULL);
847 assert(image->signature == MagickSignature);
848 if (image->debug != MagickFalse)
849 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
850 assert(exception != (ExceptionInfo *) NULL);
851 assert(exception->signature == MagickSignature);
852 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
853 {
854 InheritException(exception,&image->exception);
855 return(MagickFalse);
856 }
857 status=MagickTrue;
858 progress=0;
859 image_view=AcquireCacheView(image);
860#if defined(MAGICKCORE_OPENMP_SUPPORT)
861 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
862#endif
cristybb503372010-05-27 20:51:26 +0000863 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000864 {
865 register IndexPacket
866 *restrict indexes;
867
cristybb503372010-05-27 20:51:26 +0000868 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000869 x;
870
871 register PixelPacket
872 *restrict q;
873
874 if (status == MagickFalse)
875 continue;
876 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
877 if (q == (PixelPacket *) NULL)
878 {
879 status=MagickFalse;
880 continue;
881 }
882 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000883 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000884 {
885 if ((channel & RedChannel) != 0)
886 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
887 exception);
888 if ((channel & GreenChannel) != 0)
889 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
890 exception);
891 if ((channel & BlueChannel) != 0)
892 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
893 exception);
894 if ((channel & OpacityChannel) != 0)
895 {
896 if (image->matte == MagickFalse)
897 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
898 parameters,exception);
899 else
900 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum)
901 GetAlphaPixelComponent(q),function,number_parameters,parameters,
902 exception);
903 }
904 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
905 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
906 number_parameters,parameters,exception);
907 q++;
908 }
909 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
910 status=MagickFalse;
911 if (image->progress_monitor != (MagickProgressMonitor) NULL)
912 {
913 MagickBooleanType
914 proceed;
915
916#if defined(MAGICKCORE_OPENMP_SUPPORT)
917 #pragma omp critical (MagickCore_FunctionImageChannel)
918#endif
919 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
920 if (proceed == MagickFalse)
921 status=MagickFalse;
922 }
923 }
924 image_view=DestroyCacheView(image_view);
925 return(status);
926}
927
928/*
929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
930% %
931% %
932% %
cristy3ed852e2009-09-05 21:47:34 +0000933+ G e t I m a g e C h a n n e l E x t r e m a %
934% %
935% %
936% %
937%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
938%
939% GetImageChannelExtrema() returns the extrema of one or more image channels.
940%
941% The format of the GetImageChannelExtrema method is:
942%
943% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000944% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000945% ExceptionInfo *exception)
946%
947% A description of each parameter follows:
948%
949% o image: the image.
950%
951% o channel: the channel.
952%
953% o minima: the minimum value in the channel.
954%
955% o maxima: the maximum value in the channel.
956%
957% o exception: return any errors or warnings in this structure.
958%
959*/
960
961MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000962 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000963{
964 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
965}
966
967MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000968 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000969 ExceptionInfo *exception)
970{
971 double
972 max,
973 min;
974
975 MagickBooleanType
976 status;
977
978 assert(image != (Image *) NULL);
979 assert(image->signature == MagickSignature);
980 if (image->debug != MagickFalse)
981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
982 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +0000983 *minima=(size_t) ceil(min-0.5);
984 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000985 return(status);
986}
987
988/*
989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
990% %
991% %
992% %
993% G e t I m a g e C h a n n e l M e a n %
994% %
995% %
996% %
997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998%
999% GetImageChannelMean() returns the mean and standard deviation of one or more
1000% image channels.
1001%
1002% The format of the GetImageChannelMean method is:
1003%
1004% MagickBooleanType GetImageChannelMean(const Image *image,
1005% const ChannelType channel,double *mean,double *standard_deviation,
1006% ExceptionInfo *exception)
1007%
1008% A description of each parameter follows:
1009%
1010% o image: the image.
1011%
1012% o channel: the channel.
1013%
1014% o mean: the average value in the channel.
1015%
1016% o standard_deviation: the standard deviation of the channel.
1017%
1018% o exception: return any errors or warnings in this structure.
1019%
1020*/
1021
1022MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1023 double *standard_deviation,ExceptionInfo *exception)
1024{
1025 MagickBooleanType
1026 status;
1027
1028 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
1029 exception);
1030 return(status);
1031}
1032
1033MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1034 const ChannelType channel,double *mean,double *standard_deviation,
1035 ExceptionInfo *exception)
1036{
cristyfd9dcd42010-08-08 18:07:02 +00001037 ChannelStatistics
1038 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001039
cristyfd9dcd42010-08-08 18:07:02 +00001040 size_t
1041 channels;
cristy3ed852e2009-09-05 21:47:34 +00001042
1043 assert(image != (Image *) NULL);
1044 assert(image->signature == MagickSignature);
1045 if (image->debug != MagickFalse)
1046 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001047 channel_statistics=GetImageChannelStatistics(image,exception);
1048 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001049 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001050 channels=0;
1051 channel_statistics[AllChannels].mean=0.0;
1052 channel_statistics[AllChannels].standard_deviation=0.0;
1053 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001054 {
cristyfd9dcd42010-08-08 18:07:02 +00001055 channel_statistics[AllChannels].mean+=
1056 channel_statistics[RedChannel].mean;
1057 channel_statistics[AllChannels].standard_deviation+=
1058 channel_statistics[RedChannel].variance-
1059 channel_statistics[RedChannel].mean*
1060 channel_statistics[RedChannel].mean;
1061 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001062 }
cristyfd9dcd42010-08-08 18:07:02 +00001063 if ((channel & GreenChannel) != 0)
1064 {
1065 channel_statistics[AllChannels].mean+=
1066 channel_statistics[GreenChannel].mean;
1067 channel_statistics[AllChannels].standard_deviation+=
1068 channel_statistics[GreenChannel].variance-
1069 channel_statistics[GreenChannel].mean*
1070 channel_statistics[GreenChannel].mean;
1071 channels++;
1072 }
1073 if ((channel & BlueChannel) != 0)
1074 {
1075 channel_statistics[AllChannels].mean+=
1076 channel_statistics[BlueChannel].mean;
1077 channel_statistics[AllChannels].standard_deviation+=
1078 channel_statistics[BlueChannel].variance-
1079 channel_statistics[BlueChannel].mean*
1080 channel_statistics[BlueChannel].mean;
1081 channels++;
1082 }
1083 if (((channel & OpacityChannel) != 0) &&
1084 (image->matte != MagickFalse))
1085 {
1086 channel_statistics[AllChannels].mean+=
1087 channel_statistics[OpacityChannel].mean;
1088 channel_statistics[AllChannels].standard_deviation+=
1089 channel_statistics[OpacityChannel].variance-
1090 channel_statistics[OpacityChannel].mean*
1091 channel_statistics[OpacityChannel].mean;
1092 channels++;
1093 }
1094 if (((channel & IndexChannel) != 0) &&
1095 (image->colorspace == CMYKColorspace))
1096 {
1097 channel_statistics[AllChannels].mean+=
1098 channel_statistics[BlackChannel].mean;
1099 channel_statistics[AllChannels].standard_deviation+=
1100 channel_statistics[BlackChannel].variance-
1101 channel_statistics[BlackChannel].mean*
1102 channel_statistics[BlackChannel].mean;
1103 channels++;
1104 }
1105 channel_statistics[AllChannels].mean/=channels;
1106 channel_statistics[AllChannels].standard_deviation=
1107 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
1108 *mean=channel_statistics[AllChannels].mean;
1109 *standard_deviation=channel_statistics[AllChannels].standard_deviation;
1110 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1111 channel_statistics);
1112 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001113}
1114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% G e t I m a g e C h a n n e l K u r t o s i s %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125%
1126% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1127% image channels.
1128%
1129% The format of the GetImageChannelKurtosis method is:
1130%
1131% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1132% const ChannelType channel,double *kurtosis,double *skewness,
1133% ExceptionInfo *exception)
1134%
1135% A description of each parameter follows:
1136%
1137% o image: the image.
1138%
1139% o channel: the channel.
1140%
1141% o kurtosis: the kurtosis of the channel.
1142%
1143% o skewness: the skewness of the channel.
1144%
1145% o exception: return any errors or warnings in this structure.
1146%
1147*/
1148
1149MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1150 double *kurtosis,double *skewness,ExceptionInfo *exception)
1151{
1152 MagickBooleanType
1153 status;
1154
1155 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
1156 exception);
1157 return(status);
1158}
1159
1160MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1161 const ChannelType channel,double *kurtosis,double *skewness,
1162 ExceptionInfo *exception)
1163{
1164 double
1165 area,
1166 mean,
1167 standard_deviation,
1168 sum_squares,
1169 sum_cubes,
1170 sum_fourth_power;
1171
cristybb503372010-05-27 20:51:26 +00001172 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001173 y;
1174
1175 assert(image != (Image *) NULL);
1176 assert(image->signature == MagickSignature);
1177 if (image->debug != MagickFalse)
1178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1179 *kurtosis=0.0;
1180 *skewness=0.0;
1181 area=0.0;
1182 mean=0.0;
1183 standard_deviation=0.0;
1184 sum_squares=0.0;
1185 sum_cubes=0.0;
1186 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001187 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001188 {
1189 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001190 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001191
1192 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001193 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001194
cristybb503372010-05-27 20:51:26 +00001195 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001196 x;
1197
1198 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1199 if (p == (const PixelPacket *) NULL)
1200 break;
1201 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001202 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001203 {
1204 if ((channel & RedChannel) != 0)
1205 {
cristyce70c172010-01-07 17:15:30 +00001206 mean+=GetRedPixelComponent(p);
1207 sum_squares+=(double) p->red*GetRedPixelComponent(p);
1208 sum_cubes+=(double) p->red*p->red*GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001209 sum_fourth_power+=(double) p->red*p->red*p->red*
1210 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001211 area++;
1212 }
1213 if ((channel & GreenChannel) != 0)
1214 {
cristyce70c172010-01-07 17:15:30 +00001215 mean+=GetGreenPixelComponent(p);
1216 sum_squares+=(double) p->green*GetGreenPixelComponent(p);
1217 sum_cubes+=(double) p->green*p->green*GetGreenPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001218 sum_fourth_power+=(double) p->green*p->green*p->green*
1219 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001220 area++;
1221 }
1222 if ((channel & BlueChannel) != 0)
1223 {
cristyce70c172010-01-07 17:15:30 +00001224 mean+=GetBluePixelComponent(p);
1225 sum_squares+=(double) p->blue*GetBluePixelComponent(p);
1226 sum_cubes+=(double) p->blue*p->blue*GetBluePixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001227 sum_fourth_power+=(double) p->blue*p->blue*p->blue*
1228 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001229 area++;
1230 }
1231 if ((channel & OpacityChannel) != 0)
1232 {
cristyce70c172010-01-07 17:15:30 +00001233 mean+=GetOpacityPixelComponent(p);
1234 sum_squares+=(double) p->opacity*GetOpacityPixelComponent(p);
1235 sum_cubes+=(double) p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001236 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
cristyce70c172010-01-07 17:15:30 +00001237 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001238 area++;
1239 }
1240 if (((channel & IndexChannel) != 0) &&
1241 (image->colorspace == CMYKColorspace))
1242 {
1243 mean+=indexes[x];
1244 sum_squares+=(double) indexes[x]*indexes[x];
1245 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
1246 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1247 indexes[x];
1248 area++;
1249 }
1250 p++;
1251 }
1252 }
cristybb503372010-05-27 20:51:26 +00001253 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001254 return(MagickFalse);
1255 if (area != 0.0)
1256 {
1257 mean/=area;
1258 sum_squares/=area;
1259 sum_cubes/=area;
1260 sum_fourth_power/=area;
1261 }
1262 standard_deviation=sqrt(sum_squares-(mean*mean));
1263 if (standard_deviation != 0.0)
1264 {
1265 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1266 3.0*mean*mean*mean*mean;
1267 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1268 standard_deviation;
1269 *kurtosis-=3.0;
1270 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1271 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1272 }
cristybb503372010-05-27 20:51:26 +00001273 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001274}
cristy46f08202010-01-10 04:04:21 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276/*
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278% %
1279% %
1280% %
1281% G e t I m a g e C h a n n e l R a n g e %
1282% %
1283% %
1284% %
1285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286%
1287% GetImageChannelRange() returns the range of one or more image channels.
1288%
1289% The format of the GetImageChannelRange method is:
1290%
1291% MagickBooleanType GetImageChannelRange(const Image *image,
1292% const ChannelType channel,double *minima,double *maxima,
1293% ExceptionInfo *exception)
1294%
1295% A description of each parameter follows:
1296%
1297% o image: the image.
1298%
1299% o channel: the channel.
1300%
1301% o minima: the minimum value in the channel.
1302%
1303% o maxima: the maximum value in the channel.
1304%
1305% o exception: return any errors or warnings in this structure.
1306%
1307*/
1308
1309MagickExport MagickBooleanType GetImageRange(const Image *image,
1310 double *minima,double *maxima,ExceptionInfo *exception)
1311{
1312 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1313}
1314
1315MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1316 const ChannelType channel,double *minima,double *maxima,
1317 ExceptionInfo *exception)
1318{
cristybb503372010-05-27 20:51:26 +00001319 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001320 y;
1321
1322 MagickPixelPacket
1323 pixel;
1324
1325 assert(image != (Image *) NULL);
1326 assert(image->signature == MagickSignature);
1327 if (image->debug != MagickFalse)
1328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1329 *maxima=(-1.0E-37);
1330 *minima=1.0E+37;
1331 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001332 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001333 {
1334 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001335 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001336
1337 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001338 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001339
cristybb503372010-05-27 20:51:26 +00001340 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001341 x;
1342
1343 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1344 if (p == (const PixelPacket *) NULL)
1345 break;
1346 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001347 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001348 {
1349 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1350 if ((channel & RedChannel) != 0)
1351 {
1352 if (pixel.red < *minima)
1353 *minima=(double) pixel.red;
1354 if (pixel.red > *maxima)
1355 *maxima=(double) pixel.red;
1356 }
1357 if ((channel & GreenChannel) != 0)
1358 {
1359 if (pixel.green < *minima)
1360 *minima=(double) pixel.green;
1361 if (pixel.green > *maxima)
1362 *maxima=(double) pixel.green;
1363 }
1364 if ((channel & BlueChannel) != 0)
1365 {
1366 if (pixel.blue < *minima)
1367 *minima=(double) pixel.blue;
1368 if (pixel.blue > *maxima)
1369 *maxima=(double) pixel.blue;
1370 }
1371 if ((channel & OpacityChannel) != 0)
1372 {
1373 if (pixel.opacity < *minima)
1374 *minima=(double) pixel.opacity;
1375 if (pixel.opacity > *maxima)
1376 *maxima=(double) pixel.opacity;
1377 }
1378 if (((channel & IndexChannel) != 0) &&
1379 (image->colorspace == CMYKColorspace))
1380 {
1381 if ((double) indexes[x] < *minima)
1382 *minima=(double) indexes[x];
1383 if ((double) indexes[x] > *maxima)
1384 *maxima=(double) indexes[x];
1385 }
1386 p++;
1387 }
1388 }
cristybb503372010-05-27 20:51:26 +00001389 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001390}
1391
1392/*
1393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1394% %
1395% %
1396% %
1397% 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 %
1398% %
1399% %
1400% %
1401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1402%
1403% GetImageChannelStatistics() returns statistics for each channel in the
1404% image. The statistics include the channel depth, its minima, maxima, mean,
1405% standard deviation, kurtosis and skewness. You can access the red channel
1406% mean, for example, like this:
1407%
1408% channel_statistics=GetImageChannelStatistics(image,excepton);
1409% red_mean=channel_statistics[RedChannel].mean;
1410%
1411% Use MagickRelinquishMemory() to free the statistics buffer.
1412%
1413% The format of the GetImageChannelStatistics method is:
1414%
1415% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1416% ExceptionInfo *exception)
1417%
1418% A description of each parameter follows:
1419%
1420% o image: the image.
1421%
1422% o exception: return any errors or warnings in this structure.
1423%
1424*/
cristy3ed852e2009-09-05 21:47:34 +00001425MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1426 ExceptionInfo *exception)
1427{
1428 ChannelStatistics
1429 *channel_statistics;
1430
1431 double
cristyfd9dcd42010-08-08 18:07:02 +00001432 area;
cristy3ed852e2009-09-05 21:47:34 +00001433
cristybb503372010-05-27 20:51:26 +00001434 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001435 y;
1436
1437 MagickStatusType
1438 status;
1439
1440 QuantumAny
1441 range;
1442
cristybb503372010-05-27 20:51:26 +00001443 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001444 i;
1445
1446 size_t
1447 length;
1448
cristybb503372010-05-27 20:51:26 +00001449 size_t
cristy3ed852e2009-09-05 21:47:34 +00001450 channels,
1451 depth;
1452
1453 assert(image != (Image *) NULL);
1454 assert(image->signature == MagickSignature);
1455 if (image->debug != MagickFalse)
1456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1457 length=AllChannels+1UL;
1458 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1459 sizeof(*channel_statistics));
1460 if (channel_statistics == (ChannelStatistics *) NULL)
1461 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1462 (void) ResetMagickMemory(channel_statistics,0,length*
1463 sizeof(*channel_statistics));
1464 for (i=0; i <= AllChannels; i++)
1465 {
1466 channel_statistics[i].depth=1;
1467 channel_statistics[i].maxima=(-1.0E-37);
1468 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001469 }
cristybb503372010-05-27 20:51:26 +00001470 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001471 {
1472 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001473 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001474
1475 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001476 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001477
cristybb503372010-05-27 20:51:26 +00001478 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001479 x;
1480
1481 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1482 if (p == (const PixelPacket *) NULL)
1483 break;
1484 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001485 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001486 {
1487 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1488 {
1489 depth=channel_statistics[RedChannel].depth;
1490 range=GetQuantumRange(depth);
1491 status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
1492 range) ? MagickTrue : MagickFalse;
1493 if (status != MagickFalse)
1494 {
1495 channel_statistics[RedChannel].depth++;
1496 continue;
1497 }
1498 }
1499 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1500 {
1501 depth=channel_statistics[GreenChannel].depth;
1502 range=GetQuantumRange(depth);
1503 status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
1504 range),range) ? MagickTrue : MagickFalse;
1505 if (status != MagickFalse)
1506 {
1507 channel_statistics[GreenChannel].depth++;
1508 continue;
1509 }
1510 }
1511 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1512 {
1513 depth=channel_statistics[BlueChannel].depth;
1514 range=GetQuantumRange(depth);
1515 status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
1516 range),range) ? MagickTrue : MagickFalse;
1517 if (status != MagickFalse)
1518 {
1519 channel_statistics[BlueChannel].depth++;
1520 continue;
1521 }
1522 }
1523 if (image->matte != MagickFalse)
1524 {
1525 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1526 {
1527 depth=channel_statistics[OpacityChannel].depth;
1528 range=GetQuantumRange(depth);
1529 status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
1530 p->opacity,range),range) ? MagickTrue : MagickFalse;
1531 if (status != MagickFalse)
1532 {
1533 channel_statistics[OpacityChannel].depth++;
1534 continue;
1535 }
1536 }
1537 }
1538 if (image->colorspace == CMYKColorspace)
1539 {
1540 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1541 {
1542 depth=channel_statistics[BlackChannel].depth;
1543 range=GetQuantumRange(depth);
1544 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1545 indexes[x],range),range) ? MagickTrue : MagickFalse;
1546 if (status != MagickFalse)
1547 {
1548 channel_statistics[BlackChannel].depth++;
1549 continue;
1550 }
1551 }
1552 }
1553 if ((double) p->red < channel_statistics[RedChannel].minima)
cristyce70c172010-01-07 17:15:30 +00001554 channel_statistics[RedChannel].minima=(double) GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001555 if ((double) p->red > channel_statistics[RedChannel].maxima)
cristyce70c172010-01-07 17:15:30 +00001556 channel_statistics[RedChannel].maxima=(double) GetRedPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001557 channel_statistics[RedChannel].sum+=GetRedPixelComponent(p);
1558 channel_statistics[RedChannel].sum_squared+=(double) p->red*
cristy46f08202010-01-10 04:04:21 +00001559 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001560 channel_statistics[RedChannel].sum_cubed+=(double) p->red*p->red*
cristy46f08202010-01-10 04:04:21 +00001561 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001562 channel_statistics[RedChannel].sum_fourth_power+=(double) p->red*p->red*
1563 p->red*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001564 if ((double) p->green < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001565 channel_statistics[GreenChannel].minima=(double)
1566 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001567 if ((double) p->green > channel_statistics[GreenChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001568 channel_statistics[GreenChannel].maxima=(double)
1569 GetGreenPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001570 channel_statistics[GreenChannel].sum+=GetGreenPixelComponent(p);
1571 channel_statistics[GreenChannel].sum_squared+=(double) p->green*
cristyce70c172010-01-07 17:15:30 +00001572 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001573 channel_statistics[GreenChannel].sum_cubed+=(double) p->green*p->green*
cristyce70c172010-01-07 17:15:30 +00001574 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001575 channel_statistics[GreenChannel].sum_fourth_power+=(double) p->green*
1576 p->green*p->green*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001577 if ((double) p->blue < channel_statistics[BlueChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001578 channel_statistics[BlueChannel].minima=(double)
1579 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001580 if ((double) p->blue > channel_statistics[BlueChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001581 channel_statistics[BlueChannel].maxima=(double)
1582 GetBluePixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001583 channel_statistics[BlueChannel].sum+=GetBluePixelComponent(p);
1584 channel_statistics[BlueChannel].sum_squared+=(double) p->blue*
cristyce70c172010-01-07 17:15:30 +00001585 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001586 channel_statistics[BlueChannel].sum_cubed+=(double) p->blue*p->blue*
cristyce70c172010-01-07 17:15:30 +00001587 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001588 channel_statistics[BlueChannel].sum_fourth_power+=(double) p->blue*
1589 p->blue*p->blue*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001590 if (image->matte != MagickFalse)
1591 {
1592 if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001593 channel_statistics[OpacityChannel].minima=(double)
1594 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001595 if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001596 channel_statistics[OpacityChannel].maxima=(double)
1597 GetOpacityPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001598 channel_statistics[OpacityChannel].sum+=GetOpacityPixelComponent(p);
1599 channel_statistics[OpacityChannel].sum_squared+=(double)
cristyce70c172010-01-07 17:15:30 +00001600 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001601 channel_statistics[OpacityChannel].sum_cubed+=(double) p->opacity*
cristyce70c172010-01-07 17:15:30 +00001602 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001603 channel_statistics[OpacityChannel].sum_fourth_power+=(double)
1604 p->opacity*p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001605 }
1606 if (image->colorspace == CMYKColorspace)
1607 {
1608 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1609 channel_statistics[BlackChannel].minima=(double) indexes[x];
1610 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1611 channel_statistics[BlackChannel].maxima=(double) indexes[x];
cristyfd9dcd42010-08-08 18:07:02 +00001612 channel_statistics[BlackChannel].sum+=indexes[x];
1613 channel_statistics[BlackChannel].sum_squared+=(double)
cristy3ed852e2009-09-05 21:47:34 +00001614 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001615 channel_statistics[BlackChannel].sum_cubed+=(double) indexes[x]*
cristy3ed852e2009-09-05 21:47:34 +00001616 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001617 channel_statistics[BlackChannel].sum_fourth_power+=(double)
1618 indexes[x]*indexes[x]*indexes[x]*indexes[x];
cristy3ed852e2009-09-05 21:47:34 +00001619 }
1620 x++;
1621 p++;
1622 }
1623 }
1624 area=(double) image->columns*image->rows;
1625 for (i=0; i < AllChannels; i++)
1626 {
cristyfd9dcd42010-08-08 18:07:02 +00001627 channel_statistics[i].sum/=area;
1628 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001629 channel_statistics[i].sum_cubed/=area;
1630 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001631 channel_statistics[i].mean=channel_statistics[i].sum;
1632 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1633 channel_statistics[i].standard_deviation=sqrt(
1634 channel_statistics[i].variance-(channel_statistics[i].mean*
1635 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001636 }
1637 for (i=0; i < AllChannels; i++)
1638 {
cristybb503372010-05-27 20:51:26 +00001639 channel_statistics[AllChannels].depth=(size_t) MagickMax((double)
cristy3ed852e2009-09-05 21:47:34 +00001640 channel_statistics[AllChannels].depth,(double)
1641 channel_statistics[i].depth);
1642 channel_statistics[AllChannels].minima=MagickMin(
1643 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1644 channel_statistics[AllChannels].maxima=MagickMax(
1645 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
cristyfd9dcd42010-08-08 18:07:02 +00001646 channel_statistics[AllChannels].sum+=channel_statistics[i].sum;
1647 channel_statistics[AllChannels].sum_squared+=
1648 channel_statistics[i].sum_squared;
cristya8178ed2010-08-10 17:31:59 +00001649 channel_statistics[AllChannels].sum_cubed+=channel_statistics[i].sum_cubed;
1650 channel_statistics[AllChannels].sum_fourth_power+=
1651 channel_statistics[i].sum_fourth_power;
cristy3ed852e2009-09-05 21:47:34 +00001652 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
cristyfd9dcd42010-08-08 18:07:02 +00001653 channel_statistics[AllChannels].variance+=channel_statistics[i].variance-
1654 channel_statistics[i].mean*channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001655 channel_statistics[AllChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001656 channel_statistics[i].variance-channel_statistics[i].mean*
1657 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001658 }
cristycf584452010-08-08 02:26:04 +00001659 channels=3;
1660 if (image->matte != MagickFalse)
1661 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001662 if (image->colorspace == CMYKColorspace)
1663 channels++;
cristyfd9dcd42010-08-08 18:07:02 +00001664 channel_statistics[AllChannels].sum/=channels;
1665 channel_statistics[AllChannels].sum_squared/=channels;
cristya8178ed2010-08-10 17:31:59 +00001666 channel_statistics[AllChannels].sum_cubed/=channels;
1667 channel_statistics[AllChannels].sum_fourth_power/=channels;
cristy3ed852e2009-09-05 21:47:34 +00001668 channel_statistics[AllChannels].mean/=channels;
cristyfd9dcd42010-08-08 18:07:02 +00001669 channel_statistics[AllChannels].variance/=channels;
1670 channel_statistics[AllChannels].standard_deviation=
1671 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
cristy3ed852e2009-09-05 21:47:34 +00001672 channel_statistics[AllChannels].kurtosis/=channels;
1673 channel_statistics[AllChannels].skewness/=channels;
1674 for (i=0; i <= AllChannels; i++)
1675 {
cristy3ed852e2009-09-05 21:47:34 +00001676 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001677 continue;
1678 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1679 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1680 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1681 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1682 channel_statistics[i].standard_deviation*
1683 channel_statistics[i].standard_deviation);
1684 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1685 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1686 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1687 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1688 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1689 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1690 channel_statistics[i].standard_deviation*
1691 channel_statistics[i].standard_deviation*
1692 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001693 }
1694 return(channel_statistics);
1695}