blob: d44fd0909aae939be320ef406c8510e14942a153 [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 {
cristybb503372010-05-27 20:51:26 +0000227 result=(MagickRealType) ((size_t) pixel & (size_t)
cristy351842f2010-03-07 15:27:38 +0000228 (value+0.5));
229 break;
230 }
231 case CosineEvaluateOperator:
232 {
233 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
234 QuantumScale*pixel*value))+0.5));
235 break;
236 }
237 case DivideEvaluateOperator:
238 {
239 result=pixel/(value == 0.0 ? 1.0 : value);
240 break;
241 }
242 case GaussianNoiseEvaluateOperator:
243 {
244 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
245 GaussianNoise,value);
246 break;
247 }
248 case ImpulseNoiseEvaluateOperator:
249 {
250 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
251 ImpulseNoise,value);
252 break;
253 }
254 case LaplacianNoiseEvaluateOperator:
255 {
256 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
257 LaplacianNoise,value);
258 break;
259 }
260 case LeftShiftEvaluateOperator:
261 {
cristybb503372010-05-27 20:51:26 +0000262 result=(MagickRealType) ((size_t) pixel << (size_t)
cristy351842f2010-03-07 15:27:38 +0000263 (value+0.5));
264 break;
265 }
266 case LogEvaluateOperator:
267 {
268 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
269 pixel+1.0))/log((double) (value+1.0)));
270 break;
271 }
272 case MaxEvaluateOperator:
273 {
274 result=(MagickRealType) MagickMax((double) pixel,value);
275 break;
276 }
277 case MeanEvaluateOperator:
278 {
cristy125a5a32010-05-07 13:30:52 +0000279 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000280 break;
281 }
282 case MinEvaluateOperator:
283 {
284 result=(MagickRealType) MagickMin((double) pixel,value);
285 break;
286 }
287 case MultiplicativeNoiseEvaluateOperator:
288 {
289 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
290 MultiplicativeGaussianNoise,value);
291 break;
292 }
293 case MultiplyEvaluateOperator:
294 {
295 result=(MagickRealType) (value*pixel);
296 break;
297 }
298 case OrEvaluateOperator:
299 {
cristybb503372010-05-27 20:51:26 +0000300 result=(MagickRealType) ((size_t) pixel | (size_t)
cristy351842f2010-03-07 15:27:38 +0000301 (value+0.5));
302 break;
303 }
304 case PoissonNoiseEvaluateOperator:
305 {
306 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
307 PoissonNoise,value);
308 break;
309 }
310 case PowEvaluateOperator:
311 {
312 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
313 (double) value));
314 break;
315 }
316 case RightShiftEvaluateOperator:
317 {
cristybb503372010-05-27 20:51:26 +0000318 result=(MagickRealType) ((size_t) pixel >> (size_t)
cristy351842f2010-03-07 15:27:38 +0000319 (value+0.5));
320 break;
321 }
322 case SetEvaluateOperator:
323 {
324 result=value;
325 break;
326 }
327 case SineEvaluateOperator:
328 {
329 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
330 QuantumScale*pixel*value))+0.5));
331 break;
332 }
333 case SubtractEvaluateOperator:
334 {
335 result=(MagickRealType) (pixel-value);
336 break;
337 }
338 case ThresholdEvaluateOperator:
339 {
340 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
341 QuantumRange);
342 break;
343 }
344 case ThresholdBlackEvaluateOperator:
345 {
346 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
347 break;
348 }
349 case ThresholdWhiteEvaluateOperator:
350 {
351 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
352 pixel);
353 break;
354 }
355 case UniformNoiseEvaluateOperator:
356 {
357 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
358 UniformNoise,value);
359 break;
360 }
361 case XorEvaluateOperator:
362 {
cristybb503372010-05-27 20:51:26 +0000363 result=(MagickRealType) ((size_t) pixel ^ (size_t)
cristy351842f2010-03-07 15:27:38 +0000364 (value+0.5));
365 break;
366 }
367 }
cristyd18ae7c2010-03-07 17:39:52 +0000368 return(result);
cristy351842f2010-03-07 15:27:38 +0000369}
370
371MagickExport MagickBooleanType EvaluateImage(Image *image,
372 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
373{
374 MagickBooleanType
375 status;
376
377 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
378 return(status);
379}
380
cristyd18ae7c2010-03-07 17:39:52 +0000381MagickExport Image *EvaluateImages(const Image *images,
382 const MagickEvaluateOperator op,ExceptionInfo *exception)
383{
384#define EvaluateImageTag "Evaluate/Image"
385
386 CacheView
387 *evaluate_view;
388
389 const Image
390 *next;
391
392 Image
393 *evaluate_image;
394
cristyd18ae7c2010-03-07 17:39:52 +0000395 MagickBooleanType
396 status;
397
cristy5f959472010-05-27 22:19:46 +0000398 MagickOffsetType
399 progress;
400
cristyd18ae7c2010-03-07 17:39:52 +0000401 MagickPixelPacket
402 **restrict evaluate_pixels,
403 zero;
404
405 RandomInfo
406 **restrict random_info;
407
cristybb503372010-05-27 20:51:26 +0000408 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000409 number_images;
410
cristy5f959472010-05-27 22:19:46 +0000411 ssize_t
412 y;
413
cristyd18ae7c2010-03-07 17:39:52 +0000414 /*
415 Ensure the image are the same size.
416 */
417 assert(images != (Image *) NULL);
418 assert(images->signature == MagickSignature);
419 if (images->debug != MagickFalse)
420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
421 assert(exception != (ExceptionInfo *) NULL);
422 assert(exception->signature == MagickSignature);
423 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
424 if ((next->columns != images->columns) || (next->rows != images->rows))
425 {
426 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
427 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
428 return((Image *) NULL);
429 }
430 /*
431 Initialize evaluate next attributes.
432 */
433 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
434 exception);
435 if (evaluate_image == (Image *) NULL)
436 return((Image *) NULL);
437 if (SetImageStorageClass(evaluate_image,DirectClass) == MagickFalse)
438 {
439 InheritException(exception,&evaluate_image->exception);
440 evaluate_image=DestroyImage(evaluate_image);
441 return((Image *) NULL);
442 }
443 evaluate_pixels=AcquirePixelThreadSet(images);
444 if (evaluate_pixels == (MagickPixelPacket **) NULL)
445 {
446 evaluate_image=DestroyImage(evaluate_image);
447 (void) ThrowMagickException(exception,GetMagickModule(),
448 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
449 return((Image *) NULL);
450 }
451 /*
452 Evaluate image pixels.
453 */
454 status=MagickTrue;
455 progress=0;
456 GetMagickPixelPacket(images,&zero);
457 random_info=AcquireRandomInfoThreadSet();
458 number_images=GetImageListLength(images);
459 evaluate_view=AcquireCacheView(evaluate_image);
460#if defined(MAGICKCORE_OPENMP_SUPPORT)
461 #pragma omp parallel for schedule(dynamic) shared(progress,status)
462#endif
cristybb503372010-05-27 20:51:26 +0000463 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000464 {
465 CacheView
466 *image_view;
467
468 const Image
469 *next;
470
cristy5c9e6f22010-09-17 17:31:01 +0000471 const int
472 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000473
cristyd18ae7c2010-03-07 17:39:52 +0000474 MagickPixelPacket
475 pixel;
476
477 register IndexPacket
478 *restrict evaluate_indexes;
479
cristybb503372010-05-27 20:51:26 +0000480 register ssize_t
cristyd18ae7c2010-03-07 17:39:52 +0000481 i,
cristyd18ae7c2010-03-07 17:39:52 +0000482 x;
483
484 register MagickPixelPacket
485 *evaluate_pixel;
486
487 register PixelPacket
488 *restrict q;
489
490 if (status == MagickFalse)
491 continue;
492 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,1,
493 exception);
494 if (q == (PixelPacket *) NULL)
495 {
496 status=MagickFalse;
497 continue;
498 }
499 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
500 pixel=zero;
cristyd18ae7c2010-03-07 17:39:52 +0000501 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000502 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000503 evaluate_pixel[x]=zero;
504 next=images;
cristybb503372010-05-27 20:51:26 +0000505 for (i=0; i < (ssize_t) number_images; i++)
cristyd18ae7c2010-03-07 17:39:52 +0000506 {
507 register const IndexPacket
508 *indexes;
509
510 register const PixelPacket
511 *p;
512
513 image_view=AcquireCacheView(next);
514 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
515 if (p == (const PixelPacket *) NULL)
516 {
517 image_view=DestroyCacheView(image_view);
518 break;
519 }
520 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000521 for (x=0; x < (ssize_t) next->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000522 {
523 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],p->red,
cristy4a747d12010-03-08 20:00:33 +0000524 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
cristyd18ae7c2010-03-07 17:39:52 +0000525 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],p->green,
cristy4a747d12010-03-08 20:00:33 +0000526 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].green);
cristyd18ae7c2010-03-07 17:39:52 +0000527 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],p->blue,
cristy4a747d12010-03-08 20:00:33 +0000528 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].blue);
cristyd18ae7c2010-03-07 17:39:52 +0000529 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000530 p->opacity,i == 0 ? AddEvaluateOperator : op,
531 evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000532 if (evaluate_image->colorspace == CMYKColorspace)
533 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000534 indexes[x],i == 0 ? AddEvaluateOperator : op,
535 evaluate_pixel[x].index);
cristyd18ae7c2010-03-07 17:39:52 +0000536 p++;
537 }
538 image_view=DestroyCacheView(image_view);
539 next=GetNextImageInList(next);
540 }
cristy125a5a32010-05-07 13:30:52 +0000541 if (op == MeanEvaluateOperator)
cristybb503372010-05-27 20:51:26 +0000542 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000543 {
544 evaluate_pixel[x].red/=number_images;
545 evaluate_pixel[x].green/=number_images;
546 evaluate_pixel[x].blue/=number_images;
547 evaluate_pixel[x].opacity/=number_images;
548 evaluate_pixel[x].index/=number_images;
549 }
cristybb503372010-05-27 20:51:26 +0000550 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000551 {
552 q->red=ClampToQuantum(evaluate_pixel[x].red);
553 q->green=ClampToQuantum(evaluate_pixel[x].green);
554 q->blue=ClampToQuantum(evaluate_pixel[x].blue);
cristyc02484c2010-03-08 00:14:58 +0000555 if (evaluate_image->matte == MagickFalse)
556 q->opacity=ClampToQuantum(evaluate_pixel[x].opacity);
557 else
558 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000559 if (evaluate_image->colorspace == CMYKColorspace)
560 evaluate_indexes[x]=ClampToQuantum(evaluate_pixel[x].index);
561 q++;
562 }
563 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
564 status=MagickFalse;
565 if (images->progress_monitor != (MagickProgressMonitor) NULL)
566 {
567 MagickBooleanType
568 proceed;
569
570#if defined(MAGICKCORE_OPENMP_SUPPORT)
571 #pragma omp critical (MagickCore_EvaluateImages)
572#endif
573 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
574 evaluate_image->rows);
575 if (proceed == MagickFalse)
576 status=MagickFalse;
577 }
578 }
579 evaluate_view=DestroyCacheView(evaluate_view);
580 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
581 random_info=DestroyRandomInfoThreadSet(random_info);
582 if (status == MagickFalse)
583 evaluate_image=DestroyImage(evaluate_image);
584 return(evaluate_image);
585}
586
cristy351842f2010-03-07 15:27:38 +0000587MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
588 const ChannelType channel,const MagickEvaluateOperator op,const double value,
589 ExceptionInfo *exception)
590{
cristy351842f2010-03-07 15:27:38 +0000591 CacheView
592 *image_view;
593
cristy351842f2010-03-07 15:27:38 +0000594 MagickBooleanType
595 status;
596
cristy5f959472010-05-27 22:19:46 +0000597 MagickOffsetType
598 progress;
599
cristy351842f2010-03-07 15:27:38 +0000600 RandomInfo
601 **restrict random_info;
602
cristy5f959472010-05-27 22:19:46 +0000603 ssize_t
604 y;
605
cristy351842f2010-03-07 15:27:38 +0000606 assert(image != (Image *) NULL);
607 assert(image->signature == MagickSignature);
608 if (image->debug != MagickFalse)
609 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
610 assert(exception != (ExceptionInfo *) NULL);
611 assert(exception->signature == MagickSignature);
612 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
613 {
614 InheritException(exception,&image->exception);
615 return(MagickFalse);
616 }
617 status=MagickTrue;
618 progress=0;
619 random_info=AcquireRandomInfoThreadSet();
620 image_view=AcquireCacheView(image);
621#if defined(MAGICKCORE_OPENMP_SUPPORT)
622 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
623#endif
cristybb503372010-05-27 20:51:26 +0000624 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000625 {
cristy5c9e6f22010-09-17 17:31:01 +0000626 const int
627 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000628
cristy351842f2010-03-07 15:27:38 +0000629 register IndexPacket
630 *restrict indexes;
631
cristy351842f2010-03-07 15:27:38 +0000632 register PixelPacket
633 *restrict q;
634
cristy5c9e6f22010-09-17 17:31:01 +0000635 register ssize_t
636 x;
637
cristy351842f2010-03-07 15:27:38 +0000638 if (status == MagickFalse)
639 continue;
640 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
641 if (q == (PixelPacket *) NULL)
642 {
643 status=MagickFalse;
644 continue;
645 }
646 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000647 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000648 {
649 if ((channel & RedChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000650 q->red=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->red,op,
651 value));
cristy351842f2010-03-07 15:27:38 +0000652 if ((channel & GreenChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000653 q->green=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->green,
654 op,value));
cristy351842f2010-03-07 15:27:38 +0000655 if ((channel & BlueChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000656 q->blue=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->blue,op,
657 value));
cristy351842f2010-03-07 15:27:38 +0000658 if ((channel & OpacityChannel) != 0)
659 {
660 if (image->matte == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000661 q->opacity=ClampToQuantum(ApplyEvaluateOperator(random_info[id],
662 q->opacity,op,value));
cristy351842f2010-03-07 15:27:38 +0000663 else
cristyd18ae7c2010-03-07 17:39:52 +0000664 q->opacity=ClampToQuantum(QuantumRange-ApplyEvaluateOperator(
665 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value));
cristy351842f2010-03-07 15:27:38 +0000666 }
667 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyd18ae7c2010-03-07 17:39:52 +0000668 indexes[x]=(IndexPacket) ClampToQuantum(ApplyEvaluateOperator(
669 random_info[id],indexes[x],op,value));
cristy351842f2010-03-07 15:27:38 +0000670 q++;
671 }
672 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
673 status=MagickFalse;
674 if (image->progress_monitor != (MagickProgressMonitor) NULL)
675 {
676 MagickBooleanType
677 proceed;
678
679#if defined(MAGICKCORE_OPENMP_SUPPORT)
680 #pragma omp critical (MagickCore_EvaluateImageChannel)
681#endif
682 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
683 if (proceed == MagickFalse)
684 status=MagickFalse;
685 }
686 }
687 image_view=DestroyCacheView(image_view);
688 random_info=DestroyRandomInfoThreadSet(random_info);
689 return(status);
690}
691
692/*
693%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
694% %
695% %
696% %
697% F u n c t i o n I m a g e %
698% %
699% %
700% %
701%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
702%
703% FunctionImage() applies a value to the image with an arithmetic, relational,
704% or logical operator to an image. Use these operations to lighten or darken
705% an image, to increase or decrease contrast in an image, or to produce the
706% "negative" of an image.
707%
708% The format of the FunctionImageChannel method is:
709%
710% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000711% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000712% const double *parameters,ExceptionInfo *exception)
713% MagickBooleanType FunctionImageChannel(Image *image,
714% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000715% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000716% ExceptionInfo *exception)
717%
718% A description of each parameter follows:
719%
720% o image: the image.
721%
722% o channel: the channel.
723%
724% o function: A channel function.
725%
726% o parameters: one or more parameters.
727%
728% o exception: return any errors or warnings in this structure.
729%
730*/
731
732static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000733 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000734 ExceptionInfo *exception)
735{
736 MagickRealType
737 result;
738
cristybb503372010-05-27 20:51:26 +0000739 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000740 i;
741
742 (void) exception;
743 result=0.0;
744 switch (function)
745 {
746 case PolynomialFunction:
747 {
748 /*
749 * Polynomial
750 * Parameters: polynomial constants, highest to lowest order
751 * For example: c0*x^3 + c1*x^2 + c2*x + c3
752 */
753 result=0.0;
cristybb503372010-05-27 20:51:26 +0000754 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000755 result = result*QuantumScale*pixel + parameters[i];
756 result *= QuantumRange;
757 break;
758 }
759 case SinusoidFunction:
760 {
761 /* Sinusoid Function
762 * Parameters: Freq, Phase, Ampl, bias
763 */
764 double freq,phase,ampl,bias;
765 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
766 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
767 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
768 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
769 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
770 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
771 break;
772 }
773 case ArcsinFunction:
774 {
775 /* Arcsin Function (peged at range limits for invalid results)
776 * Parameters: Width, Center, Range, Bias
777 */
778 double width,range,center,bias;
779 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
780 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
781 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
782 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
783 result = 2.0/width*(QuantumScale*pixel - center);
784 if ( result <= -1.0 )
785 result = bias - range/2.0;
786 else if ( result >= 1.0 )
787 result = bias + range/2.0;
788 else
789 result=range/MagickPI*asin((double)result) + bias;
790 result *= QuantumRange;
791 break;
792 }
793 case ArctanFunction:
794 {
795 /* Arctan Function
796 * Parameters: Slope, Center, Range, Bias
797 */
798 double slope,range,center,bias;
799 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
800 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
801 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
802 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
803 result = MagickPI*slope*(QuantumScale*pixel - center);
804 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
805 result) + bias ) );
806 break;
807 }
808 case UndefinedFunction:
809 break;
810 }
811 return(ClampToQuantum(result));
812}
813
814MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000815 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000816 const double *parameters,ExceptionInfo *exception)
817{
818 MagickBooleanType
819 status;
820
821 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
822 parameters,exception);
823 return(status);
824}
825
826MagickExport MagickBooleanType FunctionImageChannel(Image *image,
827 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000828 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000829 ExceptionInfo *exception)
830{
831#define FunctionImageTag "Function/Image "
832
833 CacheView
834 *image_view;
835
cristy351842f2010-03-07 15:27:38 +0000836 MagickBooleanType
837 status;
838
cristy5f959472010-05-27 22:19:46 +0000839 MagickOffsetType
840 progress;
841
842 ssize_t
843 y;
844
cristy351842f2010-03-07 15:27:38 +0000845 assert(image != (Image *) NULL);
846 assert(image->signature == MagickSignature);
847 if (image->debug != MagickFalse)
848 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
849 assert(exception != (ExceptionInfo *) NULL);
850 assert(exception->signature == MagickSignature);
851 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
852 {
853 InheritException(exception,&image->exception);
854 return(MagickFalse);
855 }
856 status=MagickTrue;
857 progress=0;
858 image_view=AcquireCacheView(image);
859#if defined(MAGICKCORE_OPENMP_SUPPORT)
860 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
861#endif
cristybb503372010-05-27 20:51:26 +0000862 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000863 {
864 register IndexPacket
865 *restrict indexes;
866
cristybb503372010-05-27 20:51:26 +0000867 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000868 x;
869
870 register PixelPacket
871 *restrict q;
872
873 if (status == MagickFalse)
874 continue;
875 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
876 if (q == (PixelPacket *) NULL)
877 {
878 status=MagickFalse;
879 continue;
880 }
881 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000882 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000883 {
884 if ((channel & RedChannel) != 0)
885 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
886 exception);
887 if ((channel & GreenChannel) != 0)
888 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
889 exception);
890 if ((channel & BlueChannel) != 0)
891 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
892 exception);
893 if ((channel & OpacityChannel) != 0)
894 {
895 if (image->matte == MagickFalse)
896 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
897 parameters,exception);
898 else
899 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum)
900 GetAlphaPixelComponent(q),function,number_parameters,parameters,
901 exception);
902 }
903 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
904 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
905 number_parameters,parameters,exception);
906 q++;
907 }
908 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
909 status=MagickFalse;
910 if (image->progress_monitor != (MagickProgressMonitor) NULL)
911 {
912 MagickBooleanType
913 proceed;
914
915#if defined(MAGICKCORE_OPENMP_SUPPORT)
916 #pragma omp critical (MagickCore_FunctionImageChannel)
917#endif
918 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
919 if (proceed == MagickFalse)
920 status=MagickFalse;
921 }
922 }
923 image_view=DestroyCacheView(image_view);
924 return(status);
925}
926
927/*
928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
929% %
930% %
931% %
cristy3ed852e2009-09-05 21:47:34 +0000932+ G e t I m a g e C h a n n e l E x t r e m a %
933% %
934% %
935% %
936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
937%
938% GetImageChannelExtrema() returns the extrema of one or more image channels.
939%
940% The format of the GetImageChannelExtrema method is:
941%
942% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000943% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000944% ExceptionInfo *exception)
945%
946% A description of each parameter follows:
947%
948% o image: the image.
949%
950% o channel: the channel.
951%
952% o minima: the minimum value in the channel.
953%
954% o maxima: the maximum value in the channel.
955%
956% o exception: return any errors or warnings in this structure.
957%
958*/
959
960MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000961 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000962{
963 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
964}
965
966MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000967 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000968 ExceptionInfo *exception)
969{
970 double
971 max,
972 min;
973
974 MagickBooleanType
975 status;
976
977 assert(image != (Image *) NULL);
978 assert(image->signature == MagickSignature);
979 if (image->debug != MagickFalse)
980 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
981 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +0000982 *minima=(size_t) ceil(min-0.5);
983 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000984 return(status);
985}
986
987/*
988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989% %
990% %
991% %
992% G e t I m a g e C h a n n e l M e a n %
993% %
994% %
995% %
996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997%
998% GetImageChannelMean() returns the mean and standard deviation of one or more
999% image channels.
1000%
1001% The format of the GetImageChannelMean method is:
1002%
1003% MagickBooleanType GetImageChannelMean(const Image *image,
1004% const ChannelType channel,double *mean,double *standard_deviation,
1005% ExceptionInfo *exception)
1006%
1007% A description of each parameter follows:
1008%
1009% o image: the image.
1010%
1011% o channel: the channel.
1012%
1013% o mean: the average value in the channel.
1014%
1015% o standard_deviation: the standard deviation of the channel.
1016%
1017% o exception: return any errors or warnings in this structure.
1018%
1019*/
1020
1021MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1022 double *standard_deviation,ExceptionInfo *exception)
1023{
1024 MagickBooleanType
1025 status;
1026
1027 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
1028 exception);
1029 return(status);
1030}
1031
1032MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1033 const ChannelType channel,double *mean,double *standard_deviation,
1034 ExceptionInfo *exception)
1035{
cristyfd9dcd42010-08-08 18:07:02 +00001036 ChannelStatistics
1037 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001038
cristyfd9dcd42010-08-08 18:07:02 +00001039 size_t
1040 channels;
cristy3ed852e2009-09-05 21:47:34 +00001041
1042 assert(image != (Image *) NULL);
1043 assert(image->signature == MagickSignature);
1044 if (image->debug != MagickFalse)
1045 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001046 channel_statistics=GetImageChannelStatistics(image,exception);
1047 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001048 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001049 channels=0;
1050 channel_statistics[AllChannels].mean=0.0;
1051 channel_statistics[AllChannels].standard_deviation=0.0;
1052 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001053 {
cristyfd9dcd42010-08-08 18:07:02 +00001054 channel_statistics[AllChannels].mean+=
1055 channel_statistics[RedChannel].mean;
1056 channel_statistics[AllChannels].standard_deviation+=
1057 channel_statistics[RedChannel].variance-
1058 channel_statistics[RedChannel].mean*
1059 channel_statistics[RedChannel].mean;
1060 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001061 }
cristyfd9dcd42010-08-08 18:07:02 +00001062 if ((channel & GreenChannel) != 0)
1063 {
1064 channel_statistics[AllChannels].mean+=
1065 channel_statistics[GreenChannel].mean;
1066 channel_statistics[AllChannels].standard_deviation+=
1067 channel_statistics[GreenChannel].variance-
1068 channel_statistics[GreenChannel].mean*
1069 channel_statistics[GreenChannel].mean;
1070 channels++;
1071 }
1072 if ((channel & BlueChannel) != 0)
1073 {
1074 channel_statistics[AllChannels].mean+=
1075 channel_statistics[BlueChannel].mean;
1076 channel_statistics[AllChannels].standard_deviation+=
1077 channel_statistics[BlueChannel].variance-
1078 channel_statistics[BlueChannel].mean*
1079 channel_statistics[BlueChannel].mean;
1080 channels++;
1081 }
1082 if (((channel & OpacityChannel) != 0) &&
1083 (image->matte != MagickFalse))
1084 {
1085 channel_statistics[AllChannels].mean+=
1086 channel_statistics[OpacityChannel].mean;
1087 channel_statistics[AllChannels].standard_deviation+=
1088 channel_statistics[OpacityChannel].variance-
1089 channel_statistics[OpacityChannel].mean*
1090 channel_statistics[OpacityChannel].mean;
1091 channels++;
1092 }
1093 if (((channel & IndexChannel) != 0) &&
1094 (image->colorspace == CMYKColorspace))
1095 {
1096 channel_statistics[AllChannels].mean+=
1097 channel_statistics[BlackChannel].mean;
1098 channel_statistics[AllChannels].standard_deviation+=
1099 channel_statistics[BlackChannel].variance-
1100 channel_statistics[BlackChannel].mean*
1101 channel_statistics[BlackChannel].mean;
1102 channels++;
1103 }
1104 channel_statistics[AllChannels].mean/=channels;
1105 channel_statistics[AllChannels].standard_deviation=
1106 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
1107 *mean=channel_statistics[AllChannels].mean;
1108 *standard_deviation=channel_statistics[AllChannels].standard_deviation;
1109 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1110 channel_statistics);
1111 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001112}
1113
1114/*
1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116% %
1117% %
1118% %
1119% G e t I m a g e C h a n n e l K u r t o s i s %
1120% %
1121% %
1122% %
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124%
1125% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1126% image channels.
1127%
1128% The format of the GetImageChannelKurtosis method is:
1129%
1130% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1131% const ChannelType channel,double *kurtosis,double *skewness,
1132% ExceptionInfo *exception)
1133%
1134% A description of each parameter follows:
1135%
1136% o image: the image.
1137%
1138% o channel: the channel.
1139%
1140% o kurtosis: the kurtosis of the channel.
1141%
1142% o skewness: the skewness of the channel.
1143%
1144% o exception: return any errors or warnings in this structure.
1145%
1146*/
1147
1148MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1149 double *kurtosis,double *skewness,ExceptionInfo *exception)
1150{
1151 MagickBooleanType
1152 status;
1153
1154 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
1155 exception);
1156 return(status);
1157}
1158
1159MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1160 const ChannelType channel,double *kurtosis,double *skewness,
1161 ExceptionInfo *exception)
1162{
1163 double
1164 area,
1165 mean,
1166 standard_deviation,
1167 sum_squares,
1168 sum_cubes,
1169 sum_fourth_power;
1170
cristybb503372010-05-27 20:51:26 +00001171 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001172 y;
1173
1174 assert(image != (Image *) NULL);
1175 assert(image->signature == MagickSignature);
1176 if (image->debug != MagickFalse)
1177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1178 *kurtosis=0.0;
1179 *skewness=0.0;
1180 area=0.0;
1181 mean=0.0;
1182 standard_deviation=0.0;
1183 sum_squares=0.0;
1184 sum_cubes=0.0;
1185 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001186 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001187 {
1188 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001189 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001190
1191 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001192 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001193
cristybb503372010-05-27 20:51:26 +00001194 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001195 x;
1196
1197 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1198 if (p == (const PixelPacket *) NULL)
1199 break;
1200 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001201 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001202 {
1203 if ((channel & RedChannel) != 0)
1204 {
cristyce70c172010-01-07 17:15:30 +00001205 mean+=GetRedPixelComponent(p);
1206 sum_squares+=(double) p->red*GetRedPixelComponent(p);
1207 sum_cubes+=(double) p->red*p->red*GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001208 sum_fourth_power+=(double) p->red*p->red*p->red*
1209 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001210 area++;
1211 }
1212 if ((channel & GreenChannel) != 0)
1213 {
cristyce70c172010-01-07 17:15:30 +00001214 mean+=GetGreenPixelComponent(p);
1215 sum_squares+=(double) p->green*GetGreenPixelComponent(p);
1216 sum_cubes+=(double) p->green*p->green*GetGreenPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001217 sum_fourth_power+=(double) p->green*p->green*p->green*
1218 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001219 area++;
1220 }
1221 if ((channel & BlueChannel) != 0)
1222 {
cristyce70c172010-01-07 17:15:30 +00001223 mean+=GetBluePixelComponent(p);
1224 sum_squares+=(double) p->blue*GetBluePixelComponent(p);
1225 sum_cubes+=(double) p->blue*p->blue*GetBluePixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001226 sum_fourth_power+=(double) p->blue*p->blue*p->blue*
1227 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001228 area++;
1229 }
1230 if ((channel & OpacityChannel) != 0)
1231 {
cristyce70c172010-01-07 17:15:30 +00001232 mean+=GetOpacityPixelComponent(p);
1233 sum_squares+=(double) p->opacity*GetOpacityPixelComponent(p);
1234 sum_cubes+=(double) p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001235 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
cristyce70c172010-01-07 17:15:30 +00001236 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001237 area++;
1238 }
1239 if (((channel & IndexChannel) != 0) &&
1240 (image->colorspace == CMYKColorspace))
1241 {
1242 mean+=indexes[x];
1243 sum_squares+=(double) indexes[x]*indexes[x];
1244 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
1245 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1246 indexes[x];
1247 area++;
1248 }
1249 p++;
1250 }
1251 }
cristybb503372010-05-27 20:51:26 +00001252 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001253 return(MagickFalse);
1254 if (area != 0.0)
1255 {
1256 mean/=area;
1257 sum_squares/=area;
1258 sum_cubes/=area;
1259 sum_fourth_power/=area;
1260 }
1261 standard_deviation=sqrt(sum_squares-(mean*mean));
1262 if (standard_deviation != 0.0)
1263 {
1264 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1265 3.0*mean*mean*mean*mean;
1266 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1267 standard_deviation;
1268 *kurtosis-=3.0;
1269 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1270 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1271 }
cristybb503372010-05-27 20:51:26 +00001272 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001273}
cristy46f08202010-01-10 04:04:21 +00001274
cristy3ed852e2009-09-05 21:47:34 +00001275/*
1276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277% %
1278% %
1279% %
1280% G e t I m a g e C h a n n e l R a n g e %
1281% %
1282% %
1283% %
1284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285%
1286% GetImageChannelRange() returns the range of one or more image channels.
1287%
1288% The format of the GetImageChannelRange method is:
1289%
1290% MagickBooleanType GetImageChannelRange(const Image *image,
1291% const ChannelType channel,double *minima,double *maxima,
1292% ExceptionInfo *exception)
1293%
1294% A description of each parameter follows:
1295%
1296% o image: the image.
1297%
1298% o channel: the channel.
1299%
1300% o minima: the minimum value in the channel.
1301%
1302% o maxima: the maximum value in the channel.
1303%
1304% o exception: return any errors or warnings in this structure.
1305%
1306*/
1307
1308MagickExport MagickBooleanType GetImageRange(const Image *image,
1309 double *minima,double *maxima,ExceptionInfo *exception)
1310{
1311 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1312}
1313
1314MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1315 const ChannelType channel,double *minima,double *maxima,
1316 ExceptionInfo *exception)
1317{
cristybb503372010-05-27 20:51:26 +00001318 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001319 y;
1320
1321 MagickPixelPacket
1322 pixel;
1323
1324 assert(image != (Image *) NULL);
1325 assert(image->signature == MagickSignature);
1326 if (image->debug != MagickFalse)
1327 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1328 *maxima=(-1.0E-37);
1329 *minima=1.0E+37;
1330 GetMagickPixelPacket(image,&pixel);
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 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1349 if ((channel & RedChannel) != 0)
1350 {
1351 if (pixel.red < *minima)
1352 *minima=(double) pixel.red;
1353 if (pixel.red > *maxima)
1354 *maxima=(double) pixel.red;
1355 }
1356 if ((channel & GreenChannel) != 0)
1357 {
1358 if (pixel.green < *minima)
1359 *minima=(double) pixel.green;
1360 if (pixel.green > *maxima)
1361 *maxima=(double) pixel.green;
1362 }
1363 if ((channel & BlueChannel) != 0)
1364 {
1365 if (pixel.blue < *minima)
1366 *minima=(double) pixel.blue;
1367 if (pixel.blue > *maxima)
1368 *maxima=(double) pixel.blue;
1369 }
1370 if ((channel & OpacityChannel) != 0)
1371 {
1372 if (pixel.opacity < *minima)
1373 *minima=(double) pixel.opacity;
1374 if (pixel.opacity > *maxima)
1375 *maxima=(double) pixel.opacity;
1376 }
1377 if (((channel & IndexChannel) != 0) &&
1378 (image->colorspace == CMYKColorspace))
1379 {
1380 if ((double) indexes[x] < *minima)
1381 *minima=(double) indexes[x];
1382 if ((double) indexes[x] > *maxima)
1383 *maxima=(double) indexes[x];
1384 }
1385 p++;
1386 }
1387 }
cristybb503372010-05-27 20:51:26 +00001388 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001389}
1390
1391/*
1392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393% %
1394% %
1395% %
1396% 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 %
1397% %
1398% %
1399% %
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401%
1402% GetImageChannelStatistics() returns statistics for each channel in the
1403% image. The statistics include the channel depth, its minima, maxima, mean,
1404% standard deviation, kurtosis and skewness. You can access the red channel
1405% mean, for example, like this:
1406%
1407% channel_statistics=GetImageChannelStatistics(image,excepton);
1408% red_mean=channel_statistics[RedChannel].mean;
1409%
1410% Use MagickRelinquishMemory() to free the statistics buffer.
1411%
1412% The format of the GetImageChannelStatistics method is:
1413%
1414% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1415% ExceptionInfo *exception)
1416%
1417% A description of each parameter follows:
1418%
1419% o image: the image.
1420%
1421% o exception: return any errors or warnings in this structure.
1422%
1423*/
cristy3ed852e2009-09-05 21:47:34 +00001424MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1425 ExceptionInfo *exception)
1426{
1427 ChannelStatistics
1428 *channel_statistics;
1429
1430 double
cristyfd9dcd42010-08-08 18:07:02 +00001431 area;
cristy3ed852e2009-09-05 21:47:34 +00001432
cristybb503372010-05-27 20:51:26 +00001433 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001434 y;
1435
1436 MagickStatusType
1437 status;
1438
1439 QuantumAny
1440 range;
1441
cristybb503372010-05-27 20:51:26 +00001442 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001443 i;
1444
1445 size_t
1446 length;
1447
cristybb503372010-05-27 20:51:26 +00001448 size_t
cristy3ed852e2009-09-05 21:47:34 +00001449 channels,
1450 depth;
1451
1452 assert(image != (Image *) NULL);
1453 assert(image->signature == MagickSignature);
1454 if (image->debug != MagickFalse)
1455 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1456 length=AllChannels+1UL;
1457 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1458 sizeof(*channel_statistics));
1459 if (channel_statistics == (ChannelStatistics *) NULL)
1460 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1461 (void) ResetMagickMemory(channel_statistics,0,length*
1462 sizeof(*channel_statistics));
1463 for (i=0; i <= AllChannels; i++)
1464 {
1465 channel_statistics[i].depth=1;
1466 channel_statistics[i].maxima=(-1.0E-37);
1467 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001468 }
cristybb503372010-05-27 20:51:26 +00001469 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001470 {
1471 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001472 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001473
1474 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001475 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001476
cristybb503372010-05-27 20:51:26 +00001477 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001478 x;
1479
1480 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1481 if (p == (const PixelPacket *) NULL)
1482 break;
1483 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001484 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001485 {
1486 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1487 {
1488 depth=channel_statistics[RedChannel].depth;
1489 range=GetQuantumRange(depth);
1490 status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
1491 range) ? MagickTrue : MagickFalse;
1492 if (status != MagickFalse)
1493 {
1494 channel_statistics[RedChannel].depth++;
1495 continue;
1496 }
1497 }
1498 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1499 {
1500 depth=channel_statistics[GreenChannel].depth;
1501 range=GetQuantumRange(depth);
1502 status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
1503 range),range) ? MagickTrue : MagickFalse;
1504 if (status != MagickFalse)
1505 {
1506 channel_statistics[GreenChannel].depth++;
1507 continue;
1508 }
1509 }
1510 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1511 {
1512 depth=channel_statistics[BlueChannel].depth;
1513 range=GetQuantumRange(depth);
1514 status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
1515 range),range) ? MagickTrue : MagickFalse;
1516 if (status != MagickFalse)
1517 {
1518 channel_statistics[BlueChannel].depth++;
1519 continue;
1520 }
1521 }
1522 if (image->matte != MagickFalse)
1523 {
1524 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1525 {
1526 depth=channel_statistics[OpacityChannel].depth;
1527 range=GetQuantumRange(depth);
1528 status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
1529 p->opacity,range),range) ? MagickTrue : MagickFalse;
1530 if (status != MagickFalse)
1531 {
1532 channel_statistics[OpacityChannel].depth++;
1533 continue;
1534 }
1535 }
1536 }
1537 if (image->colorspace == CMYKColorspace)
1538 {
1539 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1540 {
1541 depth=channel_statistics[BlackChannel].depth;
1542 range=GetQuantumRange(depth);
1543 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1544 indexes[x],range),range) ? MagickTrue : MagickFalse;
1545 if (status != MagickFalse)
1546 {
1547 channel_statistics[BlackChannel].depth++;
1548 continue;
1549 }
1550 }
1551 }
1552 if ((double) p->red < channel_statistics[RedChannel].minima)
cristyce70c172010-01-07 17:15:30 +00001553 channel_statistics[RedChannel].minima=(double) GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001554 if ((double) p->red > channel_statistics[RedChannel].maxima)
cristyce70c172010-01-07 17:15:30 +00001555 channel_statistics[RedChannel].maxima=(double) GetRedPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001556 channel_statistics[RedChannel].sum+=GetRedPixelComponent(p);
1557 channel_statistics[RedChannel].sum_squared+=(double) p->red*
cristy46f08202010-01-10 04:04:21 +00001558 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001559 channel_statistics[RedChannel].sum_cubed+=(double) p->red*p->red*
cristy46f08202010-01-10 04:04:21 +00001560 GetRedPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001561 channel_statistics[RedChannel].sum_fourth_power+=(double) p->red*p->red*
1562 p->red*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001563 if ((double) p->green < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001564 channel_statistics[GreenChannel].minima=(double)
1565 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001566 if ((double) p->green > channel_statistics[GreenChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001567 channel_statistics[GreenChannel].maxima=(double)
1568 GetGreenPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001569 channel_statistics[GreenChannel].sum+=GetGreenPixelComponent(p);
1570 channel_statistics[GreenChannel].sum_squared+=(double) p->green*
cristyce70c172010-01-07 17:15:30 +00001571 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001572 channel_statistics[GreenChannel].sum_cubed+=(double) p->green*p->green*
cristyce70c172010-01-07 17:15:30 +00001573 GetGreenPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001574 channel_statistics[GreenChannel].sum_fourth_power+=(double) p->green*
1575 p->green*p->green*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001576 if ((double) p->blue < channel_statistics[BlueChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001577 channel_statistics[BlueChannel].minima=(double)
1578 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001579 if ((double) p->blue > channel_statistics[BlueChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001580 channel_statistics[BlueChannel].maxima=(double)
1581 GetBluePixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001582 channel_statistics[BlueChannel].sum+=GetBluePixelComponent(p);
1583 channel_statistics[BlueChannel].sum_squared+=(double) p->blue*
cristyce70c172010-01-07 17:15:30 +00001584 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001585 channel_statistics[BlueChannel].sum_cubed+=(double) p->blue*p->blue*
cristyce70c172010-01-07 17:15:30 +00001586 GetBluePixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001587 channel_statistics[BlueChannel].sum_fourth_power+=(double) p->blue*
1588 p->blue*p->blue*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001589 if (image->matte != MagickFalse)
1590 {
1591 if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001592 channel_statistics[OpacityChannel].minima=(double)
1593 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001594 if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001595 channel_statistics[OpacityChannel].maxima=(double)
1596 GetOpacityPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001597 channel_statistics[OpacityChannel].sum+=GetOpacityPixelComponent(p);
1598 channel_statistics[OpacityChannel].sum_squared+=(double)
cristyce70c172010-01-07 17:15:30 +00001599 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001600 channel_statistics[OpacityChannel].sum_cubed+=(double) p->opacity*
cristyce70c172010-01-07 17:15:30 +00001601 p->opacity*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001602 channel_statistics[OpacityChannel].sum_fourth_power+=(double)
1603 p->opacity*p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001604 }
1605 if (image->colorspace == CMYKColorspace)
1606 {
1607 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1608 channel_statistics[BlackChannel].minima=(double) indexes[x];
1609 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1610 channel_statistics[BlackChannel].maxima=(double) indexes[x];
cristyfd9dcd42010-08-08 18:07:02 +00001611 channel_statistics[BlackChannel].sum+=indexes[x];
1612 channel_statistics[BlackChannel].sum_squared+=(double)
cristy3ed852e2009-09-05 21:47:34 +00001613 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001614 channel_statistics[BlackChannel].sum_cubed+=(double) indexes[x]*
cristy3ed852e2009-09-05 21:47:34 +00001615 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001616 channel_statistics[BlackChannel].sum_fourth_power+=(double)
1617 indexes[x]*indexes[x]*indexes[x]*indexes[x];
cristy3ed852e2009-09-05 21:47:34 +00001618 }
1619 x++;
1620 p++;
1621 }
1622 }
1623 area=(double) image->columns*image->rows;
1624 for (i=0; i < AllChannels; i++)
1625 {
cristyfd9dcd42010-08-08 18:07:02 +00001626 channel_statistics[i].sum/=area;
1627 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001628 channel_statistics[i].sum_cubed/=area;
1629 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001630 channel_statistics[i].mean=channel_statistics[i].sum;
1631 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1632 channel_statistics[i].standard_deviation=sqrt(
1633 channel_statistics[i].variance-(channel_statistics[i].mean*
1634 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001635 }
1636 for (i=0; i < AllChannels; i++)
1637 {
cristybb503372010-05-27 20:51:26 +00001638 channel_statistics[AllChannels].depth=(size_t) MagickMax((double)
cristy3ed852e2009-09-05 21:47:34 +00001639 channel_statistics[AllChannels].depth,(double)
1640 channel_statistics[i].depth);
1641 channel_statistics[AllChannels].minima=MagickMin(
1642 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1643 channel_statistics[AllChannels].maxima=MagickMax(
1644 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
cristyfd9dcd42010-08-08 18:07:02 +00001645 channel_statistics[AllChannels].sum+=channel_statistics[i].sum;
1646 channel_statistics[AllChannels].sum_squared+=
1647 channel_statistics[i].sum_squared;
cristya8178ed2010-08-10 17:31:59 +00001648 channel_statistics[AllChannels].sum_cubed+=channel_statistics[i].sum_cubed;
1649 channel_statistics[AllChannels].sum_fourth_power+=
1650 channel_statistics[i].sum_fourth_power;
cristy3ed852e2009-09-05 21:47:34 +00001651 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
cristyfd9dcd42010-08-08 18:07:02 +00001652 channel_statistics[AllChannels].variance+=channel_statistics[i].variance-
1653 channel_statistics[i].mean*channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001654 channel_statistics[AllChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001655 channel_statistics[i].variance-channel_statistics[i].mean*
1656 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001657 }
cristycf584452010-08-08 02:26:04 +00001658 channels=3;
1659 if (image->matte != MagickFalse)
1660 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001661 if (image->colorspace == CMYKColorspace)
1662 channels++;
cristyfd9dcd42010-08-08 18:07:02 +00001663 channel_statistics[AllChannels].sum/=channels;
1664 channel_statistics[AllChannels].sum_squared/=channels;
cristya8178ed2010-08-10 17:31:59 +00001665 channel_statistics[AllChannels].sum_cubed/=channels;
1666 channel_statistics[AllChannels].sum_fourth_power/=channels;
cristy3ed852e2009-09-05 21:47:34 +00001667 channel_statistics[AllChannels].mean/=channels;
cristyfd9dcd42010-08-08 18:07:02 +00001668 channel_statistics[AllChannels].variance/=channels;
1669 channel_statistics[AllChannels].standard_deviation=
1670 sqrt(channel_statistics[AllChannels].standard_deviation/channels);
cristy3ed852e2009-09-05 21:47:34 +00001671 channel_statistics[AllChannels].kurtosis/=channels;
1672 channel_statistics[AllChannels].skewness/=channels;
1673 for (i=0; i <= AllChannels; i++)
1674 {
cristy3ed852e2009-09-05 21:47:34 +00001675 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001676 continue;
1677 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1678 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1679 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1680 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1681 channel_statistics[i].standard_deviation*
1682 channel_statistics[i].standard_deviation);
1683 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1684 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1685 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1686 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1687 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1688 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1689 channel_statistics[i].standard_deviation*
1690 channel_statistics[i].standard_deviation*
1691 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001692 }
1693 return(channel_statistics);
1694}