blob: 2cef7358c35d9c7fe3ad74321893ec287b5ab427 [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]);
144 pixels=(MagickPixelPacket **) RelinquishAlignedMemory(pixels);
145 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();
161 pixels=(MagickPixelPacket **) AcquireAlignedMemory(number_threads,
162 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;
203 case AddEvaluateOperator:
204 {
205 result=(MagickRealType) (pixel+value);
206 break;
207 }
208 case AddModulusEvaluateOperator:
209 {
210 /*
211 This returns a 'floored modulus' of the addition which is a
212 positive result. It differs from % or fmod() which returns a
213 'truncated modulus' result, where floor() is replaced by trunc()
214 and could return a negative result (which is clipped).
215 */
216 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000217 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000218 break;
219 }
220 case AndEvaluateOperator:
221 {
cristybb503372010-05-27 20:51:26 +0000222 result=(MagickRealType) ((size_t) pixel & (size_t)
cristy351842f2010-03-07 15:27:38 +0000223 (value+0.5));
224 break;
225 }
226 case CosineEvaluateOperator:
227 {
228 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
229 QuantumScale*pixel*value))+0.5));
230 break;
231 }
232 case DivideEvaluateOperator:
233 {
234 result=pixel/(value == 0.0 ? 1.0 : value);
235 break;
236 }
237 case GaussianNoiseEvaluateOperator:
238 {
239 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
240 GaussianNoise,value);
241 break;
242 }
243 case ImpulseNoiseEvaluateOperator:
244 {
245 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
246 ImpulseNoise,value);
247 break;
248 }
249 case LaplacianNoiseEvaluateOperator:
250 {
251 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
252 LaplacianNoise,value);
253 break;
254 }
255 case LeftShiftEvaluateOperator:
256 {
cristybb503372010-05-27 20:51:26 +0000257 result=(MagickRealType) ((size_t) pixel << (size_t)
cristy351842f2010-03-07 15:27:38 +0000258 (value+0.5));
259 break;
260 }
261 case LogEvaluateOperator:
262 {
263 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
264 pixel+1.0))/log((double) (value+1.0)));
265 break;
266 }
267 case MaxEvaluateOperator:
268 {
269 result=(MagickRealType) MagickMax((double) pixel,value);
270 break;
271 }
272 case MeanEvaluateOperator:
273 {
cristy125a5a32010-05-07 13:30:52 +0000274 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000275 break;
276 }
277 case MinEvaluateOperator:
278 {
279 result=(MagickRealType) MagickMin((double) pixel,value);
280 break;
281 }
282 case MultiplicativeNoiseEvaluateOperator:
283 {
284 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
285 MultiplicativeGaussianNoise,value);
286 break;
287 }
288 case MultiplyEvaluateOperator:
289 {
290 result=(MagickRealType) (value*pixel);
291 break;
292 }
293 case OrEvaluateOperator:
294 {
cristybb503372010-05-27 20:51:26 +0000295 result=(MagickRealType) ((size_t) pixel | (size_t)
cristy351842f2010-03-07 15:27:38 +0000296 (value+0.5));
297 break;
298 }
299 case PoissonNoiseEvaluateOperator:
300 {
301 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
302 PoissonNoise,value);
303 break;
304 }
305 case PowEvaluateOperator:
306 {
307 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
308 (double) value));
309 break;
310 }
311 case RightShiftEvaluateOperator:
312 {
cristybb503372010-05-27 20:51:26 +0000313 result=(MagickRealType) ((size_t) pixel >> (size_t)
cristy351842f2010-03-07 15:27:38 +0000314 (value+0.5));
315 break;
316 }
317 case SetEvaluateOperator:
318 {
319 result=value;
320 break;
321 }
322 case SineEvaluateOperator:
323 {
324 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
325 QuantumScale*pixel*value))+0.5));
326 break;
327 }
328 case SubtractEvaluateOperator:
329 {
330 result=(MagickRealType) (pixel-value);
331 break;
332 }
333 case ThresholdEvaluateOperator:
334 {
335 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
336 QuantumRange);
337 break;
338 }
339 case ThresholdBlackEvaluateOperator:
340 {
341 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
342 break;
343 }
344 case ThresholdWhiteEvaluateOperator:
345 {
346 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
347 pixel);
348 break;
349 }
350 case UniformNoiseEvaluateOperator:
351 {
352 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
353 UniformNoise,value);
354 break;
355 }
356 case XorEvaluateOperator:
357 {
cristybb503372010-05-27 20:51:26 +0000358 result=(MagickRealType) ((size_t) pixel ^ (size_t)
cristy351842f2010-03-07 15:27:38 +0000359 (value+0.5));
360 break;
361 }
362 }
cristyd18ae7c2010-03-07 17:39:52 +0000363 return(result);
cristy351842f2010-03-07 15:27:38 +0000364}
365
366MagickExport MagickBooleanType EvaluateImage(Image *image,
367 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
368{
369 MagickBooleanType
370 status;
371
372 status=EvaluateImageChannel(image,AllChannels,op,value,exception);
373 return(status);
374}
375
cristyd18ae7c2010-03-07 17:39:52 +0000376MagickExport Image *EvaluateImages(const Image *images,
377 const MagickEvaluateOperator op,ExceptionInfo *exception)
378{
379#define EvaluateImageTag "Evaluate/Image"
380
381 CacheView
382 *evaluate_view;
383
384 const Image
385 *next;
386
387 Image
388 *evaluate_image;
389
cristyd18ae7c2010-03-07 17:39:52 +0000390 MagickBooleanType
391 status;
392
cristy5f959472010-05-27 22:19:46 +0000393 MagickOffsetType
394 progress;
395
cristyd18ae7c2010-03-07 17:39:52 +0000396 MagickPixelPacket
397 **restrict evaluate_pixels,
398 zero;
399
400 RandomInfo
401 **restrict random_info;
402
cristybb503372010-05-27 20:51:26 +0000403 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000404 number_images;
405
cristy5f959472010-05-27 22:19:46 +0000406 ssize_t
407 y;
408
cristyd18ae7c2010-03-07 17:39:52 +0000409 /*
410 Ensure the image are the same size.
411 */
412 assert(images != (Image *) NULL);
413 assert(images->signature == MagickSignature);
414 if (images->debug != MagickFalse)
415 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
416 assert(exception != (ExceptionInfo *) NULL);
417 assert(exception->signature == MagickSignature);
418 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
419 if ((next->columns != images->columns) || (next->rows != images->rows))
420 {
421 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
422 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
423 return((Image *) NULL);
424 }
425 /*
426 Initialize evaluate next attributes.
427 */
428 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
429 exception);
430 if (evaluate_image == (Image *) NULL)
431 return((Image *) NULL);
432 if (SetImageStorageClass(evaluate_image,DirectClass) == MagickFalse)
433 {
434 InheritException(exception,&evaluate_image->exception);
435 evaluate_image=DestroyImage(evaluate_image);
436 return((Image *) NULL);
437 }
438 evaluate_pixels=AcquirePixelThreadSet(images);
439 if (evaluate_pixels == (MagickPixelPacket **) NULL)
440 {
441 evaluate_image=DestroyImage(evaluate_image);
442 (void) ThrowMagickException(exception,GetMagickModule(),
443 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
444 return((Image *) NULL);
445 }
446 /*
447 Evaluate image pixels.
448 */
449 status=MagickTrue;
450 progress=0;
451 GetMagickPixelPacket(images,&zero);
452 random_info=AcquireRandomInfoThreadSet();
453 number_images=GetImageListLength(images);
454 evaluate_view=AcquireCacheView(evaluate_image);
455#if defined(MAGICKCORE_OPENMP_SUPPORT)
456 #pragma omp parallel for schedule(dynamic) shared(progress,status)
457#endif
cristybb503372010-05-27 20:51:26 +0000458 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000459 {
460 CacheView
461 *image_view;
462
463 const Image
464 *next;
465
cristy6ebe97c2010-07-03 01:17:28 +0000466 int
467 id;
468
cristyd18ae7c2010-03-07 17:39:52 +0000469 MagickPixelPacket
470 pixel;
471
472 register IndexPacket
473 *restrict evaluate_indexes;
474
cristybb503372010-05-27 20:51:26 +0000475 register ssize_t
cristyd18ae7c2010-03-07 17:39:52 +0000476 i,
cristyd18ae7c2010-03-07 17:39:52 +0000477 x;
478
479 register MagickPixelPacket
480 *evaluate_pixel;
481
482 register PixelPacket
483 *restrict q;
484
485 if (status == MagickFalse)
486 continue;
487 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,1,
488 exception);
489 if (q == (PixelPacket *) NULL)
490 {
491 status=MagickFalse;
492 continue;
493 }
494 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
495 pixel=zero;
496 id=GetOpenMPThreadId();
497 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000498 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000499 evaluate_pixel[x]=zero;
500 next=images;
cristybb503372010-05-27 20:51:26 +0000501 for (i=0; i < (ssize_t) number_images; i++)
cristyd18ae7c2010-03-07 17:39:52 +0000502 {
503 register const IndexPacket
504 *indexes;
505
506 register const PixelPacket
507 *p;
508
509 image_view=AcquireCacheView(next);
510 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
511 if (p == (const PixelPacket *) NULL)
512 {
513 image_view=DestroyCacheView(image_view);
514 break;
515 }
516 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000517 for (x=0; x < (ssize_t) next->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000518 {
519 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],p->red,
cristy4a747d12010-03-08 20:00:33 +0000520 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
cristyd18ae7c2010-03-07 17:39:52 +0000521 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],p->green,
cristy4a747d12010-03-08 20:00:33 +0000522 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].green);
cristyd18ae7c2010-03-07 17:39:52 +0000523 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],p->blue,
cristy4a747d12010-03-08 20:00:33 +0000524 i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].blue);
cristyd18ae7c2010-03-07 17:39:52 +0000525 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000526 p->opacity,i == 0 ? AddEvaluateOperator : op,
527 evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000528 if (evaluate_image->colorspace == CMYKColorspace)
529 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
cristy4a747d12010-03-08 20:00:33 +0000530 indexes[x],i == 0 ? AddEvaluateOperator : op,
531 evaluate_pixel[x].index);
cristyd18ae7c2010-03-07 17:39:52 +0000532 p++;
533 }
534 image_view=DestroyCacheView(image_view);
535 next=GetNextImageInList(next);
536 }
cristy125a5a32010-05-07 13:30:52 +0000537 if (op == MeanEvaluateOperator)
cristybb503372010-05-27 20:51:26 +0000538 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000539 {
540 evaluate_pixel[x].red/=number_images;
541 evaluate_pixel[x].green/=number_images;
542 evaluate_pixel[x].blue/=number_images;
543 evaluate_pixel[x].opacity/=number_images;
544 evaluate_pixel[x].index/=number_images;
545 }
cristybb503372010-05-27 20:51:26 +0000546 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristyd18ae7c2010-03-07 17:39:52 +0000547 {
548 q->red=ClampToQuantum(evaluate_pixel[x].red);
549 q->green=ClampToQuantum(evaluate_pixel[x].green);
550 q->blue=ClampToQuantum(evaluate_pixel[x].blue);
cristyc02484c2010-03-08 00:14:58 +0000551 if (evaluate_image->matte == MagickFalse)
552 q->opacity=ClampToQuantum(evaluate_pixel[x].opacity);
553 else
554 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[x].opacity);
cristyd18ae7c2010-03-07 17:39:52 +0000555 if (evaluate_image->colorspace == CMYKColorspace)
556 evaluate_indexes[x]=ClampToQuantum(evaluate_pixel[x].index);
557 q++;
558 }
559 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
560 status=MagickFalse;
561 if (images->progress_monitor != (MagickProgressMonitor) NULL)
562 {
563 MagickBooleanType
564 proceed;
565
566#if defined(MAGICKCORE_OPENMP_SUPPORT)
567 #pragma omp critical (MagickCore_EvaluateImages)
568#endif
569 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
570 evaluate_image->rows);
571 if (proceed == MagickFalse)
572 status=MagickFalse;
573 }
574 }
575 evaluate_view=DestroyCacheView(evaluate_view);
576 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
577 random_info=DestroyRandomInfoThreadSet(random_info);
578 if (status == MagickFalse)
579 evaluate_image=DestroyImage(evaluate_image);
580 return(evaluate_image);
581}
582
cristy351842f2010-03-07 15:27:38 +0000583MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
584 const ChannelType channel,const MagickEvaluateOperator op,const double value,
585 ExceptionInfo *exception)
586{
cristy351842f2010-03-07 15:27:38 +0000587 CacheView
588 *image_view;
589
cristy351842f2010-03-07 15:27:38 +0000590 MagickBooleanType
591 status;
592
cristy5f959472010-05-27 22:19:46 +0000593 MagickOffsetType
594 progress;
595
cristy351842f2010-03-07 15:27:38 +0000596 RandomInfo
597 **restrict random_info;
598
cristy5f959472010-05-27 22:19:46 +0000599 ssize_t
600 y;
601
cristy351842f2010-03-07 15:27:38 +0000602 assert(image != (Image *) NULL);
603 assert(image->signature == MagickSignature);
604 if (image->debug != MagickFalse)
605 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
606 assert(exception != (ExceptionInfo *) NULL);
607 assert(exception->signature == MagickSignature);
608 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
609 {
610 InheritException(exception,&image->exception);
611 return(MagickFalse);
612 }
613 status=MagickTrue;
614 progress=0;
615 random_info=AcquireRandomInfoThreadSet();
616 image_view=AcquireCacheView(image);
617#if defined(MAGICKCORE_OPENMP_SUPPORT)
618 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
619#endif
cristybb503372010-05-27 20:51:26 +0000620 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000621 {
cristy6ebe97c2010-07-03 01:17:28 +0000622 int
623 id;
624
cristy351842f2010-03-07 15:27:38 +0000625 register IndexPacket
626 *restrict indexes;
627
cristybb503372010-05-27 20:51:26 +0000628 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000629 x;
630
631 register PixelPacket
632 *restrict q;
633
634 if (status == MagickFalse)
635 continue;
636 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
637 if (q == (PixelPacket *) NULL)
638 {
639 status=MagickFalse;
640 continue;
641 }
642 indexes=GetCacheViewAuthenticIndexQueue(image_view);
643 id=GetOpenMPThreadId();
cristybb503372010-05-27 20:51:26 +0000644 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000645 {
646 if ((channel & RedChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000647 q->red=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->red,op,
648 value));
cristy351842f2010-03-07 15:27:38 +0000649 if ((channel & GreenChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000650 q->green=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->green,
651 op,value));
cristy351842f2010-03-07 15:27:38 +0000652 if ((channel & BlueChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000653 q->blue=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->blue,op,
654 value));
cristy351842f2010-03-07 15:27:38 +0000655 if ((channel & OpacityChannel) != 0)
656 {
657 if (image->matte == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000658 q->opacity=ClampToQuantum(ApplyEvaluateOperator(random_info[id],
659 q->opacity,op,value));
cristy351842f2010-03-07 15:27:38 +0000660 else
cristyd18ae7c2010-03-07 17:39:52 +0000661 q->opacity=ClampToQuantum(QuantumRange-ApplyEvaluateOperator(
662 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value));
cristy351842f2010-03-07 15:27:38 +0000663 }
664 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyd18ae7c2010-03-07 17:39:52 +0000665 indexes[x]=(IndexPacket) ClampToQuantum(ApplyEvaluateOperator(
666 random_info[id],indexes[x],op,value));
cristy351842f2010-03-07 15:27:38 +0000667 q++;
668 }
669 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
670 status=MagickFalse;
671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
672 {
673 MagickBooleanType
674 proceed;
675
676#if defined(MAGICKCORE_OPENMP_SUPPORT)
677 #pragma omp critical (MagickCore_EvaluateImageChannel)
678#endif
679 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
680 if (proceed == MagickFalse)
681 status=MagickFalse;
682 }
683 }
684 image_view=DestroyCacheView(image_view);
685 random_info=DestroyRandomInfoThreadSet(random_info);
686 return(status);
687}
688
689/*
690%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
691% %
692% %
693% %
694% F u n c t i o n I m a g e %
695% %
696% %
697% %
698%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699%
700% FunctionImage() applies a value to the image with an arithmetic, relational,
701% or logical operator to an image. Use these operations to lighten or darken
702% an image, to increase or decrease contrast in an image, or to produce the
703% "negative" of an image.
704%
705% The format of the FunctionImageChannel method is:
706%
707% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000708% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000709% const double *parameters,ExceptionInfo *exception)
710% MagickBooleanType FunctionImageChannel(Image *image,
711% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000712% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000713% ExceptionInfo *exception)
714%
715% A description of each parameter follows:
716%
717% o image: the image.
718%
719% o channel: the channel.
720%
721% o function: A channel function.
722%
723% o parameters: one or more parameters.
724%
725% o exception: return any errors or warnings in this structure.
726%
727*/
728
729static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000730 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000731 ExceptionInfo *exception)
732{
733 MagickRealType
734 result;
735
cristybb503372010-05-27 20:51:26 +0000736 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000737 i;
738
739 (void) exception;
740 result=0.0;
741 switch (function)
742 {
743 case PolynomialFunction:
744 {
745 /*
746 * Polynomial
747 * Parameters: polynomial constants, highest to lowest order
748 * For example: c0*x^3 + c1*x^2 + c2*x + c3
749 */
750 result=0.0;
cristybb503372010-05-27 20:51:26 +0000751 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000752 result = result*QuantumScale*pixel + parameters[i];
753 result *= QuantumRange;
754 break;
755 }
756 case SinusoidFunction:
757 {
758 /* Sinusoid Function
759 * Parameters: Freq, Phase, Ampl, bias
760 */
761 double freq,phase,ampl,bias;
762 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
763 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
764 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
765 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
766 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
767 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
768 break;
769 }
770 case ArcsinFunction:
771 {
772 /* Arcsin Function (peged at range limits for invalid results)
773 * Parameters: Width, Center, Range, Bias
774 */
775 double width,range,center,bias;
776 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
777 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
778 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
779 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
780 result = 2.0/width*(QuantumScale*pixel - center);
781 if ( result <= -1.0 )
782 result = bias - range/2.0;
783 else if ( result >= 1.0 )
784 result = bias + range/2.0;
785 else
786 result=range/MagickPI*asin((double)result) + bias;
787 result *= QuantumRange;
788 break;
789 }
790 case ArctanFunction:
791 {
792 /* Arctan Function
793 * Parameters: Slope, Center, Range, Bias
794 */
795 double slope,range,center,bias;
796 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
797 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
798 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
799 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
800 result = MagickPI*slope*(QuantumScale*pixel - center);
801 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
802 result) + bias ) );
803 break;
804 }
805 case UndefinedFunction:
806 break;
807 }
808 return(ClampToQuantum(result));
809}
810
811MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000812 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000813 const double *parameters,ExceptionInfo *exception)
814{
815 MagickBooleanType
816 status;
817
818 status=FunctionImageChannel(image,AllChannels,function,number_parameters,
819 parameters,exception);
820 return(status);
821}
822
823MagickExport MagickBooleanType FunctionImageChannel(Image *image,
824 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000825 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000826 ExceptionInfo *exception)
827{
828#define FunctionImageTag "Function/Image "
829
830 CacheView
831 *image_view;
832
cristy351842f2010-03-07 15:27:38 +0000833 MagickBooleanType
834 status;
835
cristy5f959472010-05-27 22:19:46 +0000836 MagickOffsetType
837 progress;
838
839 ssize_t
840 y;
841
cristy351842f2010-03-07 15:27:38 +0000842 assert(image != (Image *) NULL);
843 assert(image->signature == MagickSignature);
844 if (image->debug != MagickFalse)
845 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
846 assert(exception != (ExceptionInfo *) NULL);
847 assert(exception->signature == MagickSignature);
848 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
849 {
850 InheritException(exception,&image->exception);
851 return(MagickFalse);
852 }
853 status=MagickTrue;
854 progress=0;
855 image_view=AcquireCacheView(image);
856#if defined(MAGICKCORE_OPENMP_SUPPORT)
857 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
858#endif
cristybb503372010-05-27 20:51:26 +0000859 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000860 {
861 register IndexPacket
862 *restrict indexes;
863
cristybb503372010-05-27 20:51:26 +0000864 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000865 x;
866
867 register PixelPacket
868 *restrict q;
869
870 if (status == MagickFalse)
871 continue;
872 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
873 if (q == (PixelPacket *) NULL)
874 {
875 status=MagickFalse;
876 continue;
877 }
878 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000879 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000880 {
881 if ((channel & RedChannel) != 0)
882 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
883 exception);
884 if ((channel & GreenChannel) != 0)
885 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
886 exception);
887 if ((channel & BlueChannel) != 0)
888 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
889 exception);
890 if ((channel & OpacityChannel) != 0)
891 {
892 if (image->matte == MagickFalse)
893 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
894 parameters,exception);
895 else
896 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum)
897 GetAlphaPixelComponent(q),function,number_parameters,parameters,
898 exception);
899 }
900 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
901 indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
902 number_parameters,parameters,exception);
903 q++;
904 }
905 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
906 status=MagickFalse;
907 if (image->progress_monitor != (MagickProgressMonitor) NULL)
908 {
909 MagickBooleanType
910 proceed;
911
912#if defined(MAGICKCORE_OPENMP_SUPPORT)
913 #pragma omp critical (MagickCore_FunctionImageChannel)
914#endif
915 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
916 if (proceed == MagickFalse)
917 status=MagickFalse;
918 }
919 }
920 image_view=DestroyCacheView(image_view);
921 return(status);
922}
923
924/*
925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
926% %
927% %
928% %
cristy3ed852e2009-09-05 21:47:34 +0000929+ G e t I m a g e C h a n n e l E x t r e m a %
930% %
931% %
932% %
933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
934%
935% GetImageChannelExtrema() returns the extrema of one or more image channels.
936%
937% The format of the GetImageChannelExtrema method is:
938%
939% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000940% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000941% ExceptionInfo *exception)
942%
943% A description of each parameter follows:
944%
945% o image: the image.
946%
947% o channel: the channel.
948%
949% o minima: the minimum value in the channel.
950%
951% o maxima: the maximum value in the channel.
952%
953% o exception: return any errors or warnings in this structure.
954%
955*/
956
957MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000958 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000959{
960 return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
961}
962
963MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +0000964 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +0000965 ExceptionInfo *exception)
966{
967 double
968 max,
969 min;
970
971 MagickBooleanType
972 status;
973
974 assert(image != (Image *) NULL);
975 assert(image->signature == MagickSignature);
976 if (image->debug != MagickFalse)
977 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
978 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +0000979 *minima=(size_t) ceil(min-0.5);
980 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000981 return(status);
982}
983
984/*
985%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
986% %
987% %
988% %
989% G e t I m a g e C h a n n e l M e a n %
990% %
991% %
992% %
993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994%
995% GetImageChannelMean() returns the mean and standard deviation of one or more
996% image channels.
997%
998% The format of the GetImageChannelMean method is:
999%
1000% MagickBooleanType GetImageChannelMean(const Image *image,
1001% const ChannelType channel,double *mean,double *standard_deviation,
1002% ExceptionInfo *exception)
1003%
1004% A description of each parameter follows:
1005%
1006% o image: the image.
1007%
1008% o channel: the channel.
1009%
1010% o mean: the average value in the channel.
1011%
1012% o standard_deviation: the standard deviation of the channel.
1013%
1014% o exception: return any errors or warnings in this structure.
1015%
1016*/
1017
1018MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1019 double *standard_deviation,ExceptionInfo *exception)
1020{
1021 MagickBooleanType
1022 status;
1023
1024 status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
1025 exception);
1026 return(status);
1027}
1028
1029MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1030 const ChannelType channel,double *mean,double *standard_deviation,
1031 ExceptionInfo *exception)
1032{
1033 double
1034 area;
1035
cristybb503372010-05-27 20:51:26 +00001036 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001037 y;
1038
1039 assert(image != (Image *) NULL);
1040 assert(image->signature == MagickSignature);
1041 if (image->debug != MagickFalse)
1042 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1043 *mean=0.0;
1044 *standard_deviation=0.0;
1045 area=0.0;
cristybb503372010-05-27 20:51:26 +00001046 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001047 {
1048 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001049 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001050
1051 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001052 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001053
cristybb503372010-05-27 20:51:26 +00001054 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001055 x;
1056
1057 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1058 if (p == (const PixelPacket *) NULL)
1059 break;
1060 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001061 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001062 {
1063 if ((channel & RedChannel) != 0)
1064 {
cristyce70c172010-01-07 17:15:30 +00001065 *mean+=GetRedPixelComponent(p);
1066 *standard_deviation+=(double) p->red*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001067 area++;
1068 }
1069 if ((channel & GreenChannel) != 0)
1070 {
cristyce70c172010-01-07 17:15:30 +00001071 *mean+=GetGreenPixelComponent(p);
1072 *standard_deviation+=(double) p->green*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001073 area++;
1074 }
1075 if ((channel & BlueChannel) != 0)
1076 {
cristyce70c172010-01-07 17:15:30 +00001077 *mean+=GetBluePixelComponent(p);
1078 *standard_deviation+=(double) p->blue*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001079 area++;
1080 }
1081 if ((channel & OpacityChannel) != 0)
1082 {
cristyce70c172010-01-07 17:15:30 +00001083 *mean+=GetOpacityPixelComponent(p);
1084 *standard_deviation+=(double) p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001085 area++;
1086 }
1087 if (((channel & IndexChannel) != 0) &&
1088 (image->colorspace == CMYKColorspace))
1089 {
1090 *mean+=indexes[x];
1091 *standard_deviation+=(double) indexes[x]*indexes[x];
1092 area++;
1093 }
1094 p++;
1095 }
1096 }
cristybb503372010-05-27 20:51:26 +00001097 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001098 return(MagickFalse);
1099 if (area != 0)
1100 {
1101 *mean/=area;
1102 *standard_deviation/=area;
1103 }
1104 *standard_deviation=sqrt(*standard_deviation-(*mean*(*mean)));
cristybb503372010-05-27 20:51:26 +00001105 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001106}
1107
1108/*
1109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110% %
1111% %
1112% %
1113% G e t I m a g e C h a n n e l K u r t o s i s %
1114% %
1115% %
1116% %
1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118%
1119% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1120% image channels.
1121%
1122% The format of the GetImageChannelKurtosis method is:
1123%
1124% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1125% const ChannelType channel,double *kurtosis,double *skewness,
1126% ExceptionInfo *exception)
1127%
1128% A description of each parameter follows:
1129%
1130% o image: the image.
1131%
1132% o channel: the channel.
1133%
1134% o kurtosis: the kurtosis of the channel.
1135%
1136% o skewness: the skewness of the channel.
1137%
1138% o exception: return any errors or warnings in this structure.
1139%
1140*/
1141
1142MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1143 double *kurtosis,double *skewness,ExceptionInfo *exception)
1144{
1145 MagickBooleanType
1146 status;
1147
1148 status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
1149 exception);
1150 return(status);
1151}
1152
1153MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1154 const ChannelType channel,double *kurtosis,double *skewness,
1155 ExceptionInfo *exception)
1156{
1157 double
1158 area,
1159 mean,
1160 standard_deviation,
1161 sum_squares,
1162 sum_cubes,
1163 sum_fourth_power;
1164
cristybb503372010-05-27 20:51:26 +00001165 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001166 y;
1167
1168 assert(image != (Image *) NULL);
1169 assert(image->signature == MagickSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1172 *kurtosis=0.0;
1173 *skewness=0.0;
1174 area=0.0;
1175 mean=0.0;
1176 standard_deviation=0.0;
1177 sum_squares=0.0;
1178 sum_cubes=0.0;
1179 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001180 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001181 {
1182 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001183 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001184
1185 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001186 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001187
cristybb503372010-05-27 20:51:26 +00001188 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001189 x;
1190
1191 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1192 if (p == (const PixelPacket *) NULL)
1193 break;
1194 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001195 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001196 {
1197 if ((channel & RedChannel) != 0)
1198 {
cristyce70c172010-01-07 17:15:30 +00001199 mean+=GetRedPixelComponent(p);
1200 sum_squares+=(double) p->red*GetRedPixelComponent(p);
1201 sum_cubes+=(double) p->red*p->red*GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001202 sum_fourth_power+=(double) p->red*p->red*p->red*
1203 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001204 area++;
1205 }
1206 if ((channel & GreenChannel) != 0)
1207 {
cristyce70c172010-01-07 17:15:30 +00001208 mean+=GetGreenPixelComponent(p);
1209 sum_squares+=(double) p->green*GetGreenPixelComponent(p);
1210 sum_cubes+=(double) p->green*p->green*GetGreenPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001211 sum_fourth_power+=(double) p->green*p->green*p->green*
1212 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001213 area++;
1214 }
1215 if ((channel & BlueChannel) != 0)
1216 {
cristyce70c172010-01-07 17:15:30 +00001217 mean+=GetBluePixelComponent(p);
1218 sum_squares+=(double) p->blue*GetBluePixelComponent(p);
1219 sum_cubes+=(double) p->blue*p->blue*GetBluePixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001220 sum_fourth_power+=(double) p->blue*p->blue*p->blue*
1221 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001222 area++;
1223 }
1224 if ((channel & OpacityChannel) != 0)
1225 {
cristyce70c172010-01-07 17:15:30 +00001226 mean+=GetOpacityPixelComponent(p);
1227 sum_squares+=(double) p->opacity*GetOpacityPixelComponent(p);
1228 sum_cubes+=(double) p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001229 sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
cristyce70c172010-01-07 17:15:30 +00001230 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001231 area++;
1232 }
1233 if (((channel & IndexChannel) != 0) &&
1234 (image->colorspace == CMYKColorspace))
1235 {
1236 mean+=indexes[x];
1237 sum_squares+=(double) indexes[x]*indexes[x];
1238 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
1239 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1240 indexes[x];
1241 area++;
1242 }
1243 p++;
1244 }
1245 }
cristybb503372010-05-27 20:51:26 +00001246 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001247 return(MagickFalse);
1248 if (area != 0.0)
1249 {
1250 mean/=area;
1251 sum_squares/=area;
1252 sum_cubes/=area;
1253 sum_fourth_power/=area;
1254 }
1255 standard_deviation=sqrt(sum_squares-(mean*mean));
1256 if (standard_deviation != 0.0)
1257 {
1258 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1259 3.0*mean*mean*mean*mean;
1260 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1261 standard_deviation;
1262 *kurtosis-=3.0;
1263 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1264 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1265 }
cristybb503372010-05-27 20:51:26 +00001266 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001267}
cristy46f08202010-01-10 04:04:21 +00001268
cristy3ed852e2009-09-05 21:47:34 +00001269/*
1270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271% %
1272% %
1273% %
1274% G e t I m a g e C h a n n e l R a n g e %
1275% %
1276% %
1277% %
1278%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1279%
1280% GetImageChannelRange() returns the range of one or more image channels.
1281%
1282% The format of the GetImageChannelRange method is:
1283%
1284% MagickBooleanType GetImageChannelRange(const Image *image,
1285% const ChannelType channel,double *minima,double *maxima,
1286% ExceptionInfo *exception)
1287%
1288% A description of each parameter follows:
1289%
1290% o image: the image.
1291%
1292% o channel: the channel.
1293%
1294% o minima: the minimum value in the channel.
1295%
1296% o maxima: the maximum value in the channel.
1297%
1298% o exception: return any errors or warnings in this structure.
1299%
1300*/
1301
1302MagickExport MagickBooleanType GetImageRange(const Image *image,
1303 double *minima,double *maxima,ExceptionInfo *exception)
1304{
1305 return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
1306}
1307
1308MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1309 const ChannelType channel,double *minima,double *maxima,
1310 ExceptionInfo *exception)
1311{
cristybb503372010-05-27 20:51:26 +00001312 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001313 y;
1314
1315 MagickPixelPacket
1316 pixel;
1317
1318 assert(image != (Image *) NULL);
1319 assert(image->signature == MagickSignature);
1320 if (image->debug != MagickFalse)
1321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1322 *maxima=(-1.0E-37);
1323 *minima=1.0E+37;
1324 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001325 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001326 {
1327 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001328 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001329
1330 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001331 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001332
cristybb503372010-05-27 20:51:26 +00001333 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001334 x;
1335
1336 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1337 if (p == (const PixelPacket *) NULL)
1338 break;
1339 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001340 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001341 {
1342 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1343 if ((channel & RedChannel) != 0)
1344 {
1345 if (pixel.red < *minima)
1346 *minima=(double) pixel.red;
1347 if (pixel.red > *maxima)
1348 *maxima=(double) pixel.red;
1349 }
1350 if ((channel & GreenChannel) != 0)
1351 {
1352 if (pixel.green < *minima)
1353 *minima=(double) pixel.green;
1354 if (pixel.green > *maxima)
1355 *maxima=(double) pixel.green;
1356 }
1357 if ((channel & BlueChannel) != 0)
1358 {
1359 if (pixel.blue < *minima)
1360 *minima=(double) pixel.blue;
1361 if (pixel.blue > *maxima)
1362 *maxima=(double) pixel.blue;
1363 }
1364 if ((channel & OpacityChannel) != 0)
1365 {
1366 if (pixel.opacity < *minima)
1367 *minima=(double) pixel.opacity;
1368 if (pixel.opacity > *maxima)
1369 *maxima=(double) pixel.opacity;
1370 }
1371 if (((channel & IndexChannel) != 0) &&
1372 (image->colorspace == CMYKColorspace))
1373 {
1374 if ((double) indexes[x] < *minima)
1375 *minima=(double) indexes[x];
1376 if ((double) indexes[x] > *maxima)
1377 *maxima=(double) indexes[x];
1378 }
1379 p++;
1380 }
1381 }
cristybb503372010-05-27 20:51:26 +00001382 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001383}
1384
1385/*
1386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387% %
1388% %
1389% %
1390% 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 %
1391% %
1392% %
1393% %
1394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395%
1396% GetImageChannelStatistics() returns statistics for each channel in the
1397% image. The statistics include the channel depth, its minima, maxima, mean,
1398% standard deviation, kurtosis and skewness. You can access the red channel
1399% mean, for example, like this:
1400%
1401% channel_statistics=GetImageChannelStatistics(image,excepton);
1402% red_mean=channel_statistics[RedChannel].mean;
1403%
1404% Use MagickRelinquishMemory() to free the statistics buffer.
1405%
1406% The format of the GetImageChannelStatistics method is:
1407%
1408% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1409% ExceptionInfo *exception)
1410%
1411% A description of each parameter follows:
1412%
1413% o image: the image.
1414%
1415% o exception: return any errors or warnings in this structure.
1416%
1417*/
cristy3ed852e2009-09-05 21:47:34 +00001418MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1419 ExceptionInfo *exception)
1420{
1421 ChannelStatistics
1422 *channel_statistics;
1423
1424 double
1425 area,
1426 sum_squares,
1427 sum_cubes;
1428
cristybb503372010-05-27 20:51:26 +00001429 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001430 y;
1431
1432 MagickStatusType
1433 status;
1434
1435 QuantumAny
1436 range;
1437
cristybb503372010-05-27 20:51:26 +00001438 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001439 i;
1440
1441 size_t
1442 length;
1443
cristybb503372010-05-27 20:51:26 +00001444 size_t
cristy3ed852e2009-09-05 21:47:34 +00001445 channels,
1446 depth;
1447
1448 assert(image != (Image *) NULL);
1449 assert(image->signature == MagickSignature);
1450 if (image->debug != MagickFalse)
1451 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1452 length=AllChannels+1UL;
1453 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1454 sizeof(*channel_statistics));
1455 if (channel_statistics == (ChannelStatistics *) NULL)
1456 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1457 (void) ResetMagickMemory(channel_statistics,0,length*
1458 sizeof(*channel_statistics));
1459 for (i=0; i <= AllChannels; i++)
1460 {
1461 channel_statistics[i].depth=1;
1462 channel_statistics[i].maxima=(-1.0E-37);
1463 channel_statistics[i].minima=1.0E+37;
1464 channel_statistics[i].mean=0.0;
1465 channel_statistics[i].standard_deviation=0.0;
1466 channel_statistics[i].kurtosis=0.0;
1467 channel_statistics[i].skewness=0.0;
1468 }
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);
1556 channel_statistics[RedChannel].mean+=GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001557 channel_statistics[RedChannel].standard_deviation+=(double) p->red*
1558 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001559 channel_statistics[RedChannel].kurtosis+=(double) p->red*p->red*
cristyce70c172010-01-07 17:15:30 +00001560 p->red*GetRedPixelComponent(p);
cristy46f08202010-01-10 04:04:21 +00001561 channel_statistics[RedChannel].skewness+=(double) p->red*p->red*
1562 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);
cristyce70c172010-01-07 17:15:30 +00001569 channel_statistics[GreenChannel].mean+=GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001570 channel_statistics[GreenChannel].standard_deviation+=(double) p->green*
cristyce70c172010-01-07 17:15:30 +00001571 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001572 channel_statistics[GreenChannel].kurtosis+=(double) p->green*p->green*
cristyce70c172010-01-07 17:15:30 +00001573 p->green*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001574 channel_statistics[GreenChannel].skewness+=(double) p->green*p->green*
cristyce70c172010-01-07 17:15:30 +00001575 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);
cristyce70c172010-01-07 17:15:30 +00001582 channel_statistics[BlueChannel].mean+=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001583 channel_statistics[BlueChannel].standard_deviation+=(double) p->blue*
cristyce70c172010-01-07 17:15:30 +00001584 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001585 channel_statistics[BlueChannel].kurtosis+=(double) p->blue*p->blue*
cristyce70c172010-01-07 17:15:30 +00001586 p->blue*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001587 channel_statistics[BlueChannel].skewness+=(double) p->blue*p->blue*
cristyce70c172010-01-07 17:15:30 +00001588 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);
cristyce70c172010-01-07 17:15:30 +00001597 channel_statistics[OpacityChannel].mean+=GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001598 channel_statistics[OpacityChannel].standard_deviation+=(double)
cristyce70c172010-01-07 17:15:30 +00001599 p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001600 channel_statistics[OpacityChannel].kurtosis+=(double) p->opacity*
cristyce70c172010-01-07 17:15:30 +00001601 p->opacity*p->opacity*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001602 channel_statistics[OpacityChannel].skewness+=(double) p->opacity*
cristyce70c172010-01-07 17:15:30 +00001603 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];
1611 channel_statistics[BlackChannel].mean+=indexes[x];
1612 channel_statistics[BlackChannel].standard_deviation+=(double)
1613 indexes[x]*indexes[x];
1614 channel_statistics[BlackChannel].kurtosis+=(double) indexes[x]*
1615 indexes[x]*indexes[x]*indexes[x];
1616 channel_statistics[BlackChannel].skewness+=(double) indexes[x]*
1617 indexes[x]*indexes[x];
1618 }
1619 x++;
1620 p++;
1621 }
1622 }
1623 area=(double) image->columns*image->rows;
1624 for (i=0; i < AllChannels; i++)
1625 {
1626 channel_statistics[i].mean/=area;
1627 channel_statistics[i].standard_deviation/=area;
1628 channel_statistics[i].kurtosis/=area;
1629 channel_statistics[i].skewness/=area;
1630 }
1631 for (i=0; i < AllChannels; i++)
1632 {
cristybb503372010-05-27 20:51:26 +00001633 channel_statistics[AllChannels].depth=(size_t) MagickMax((double)
cristy3ed852e2009-09-05 21:47:34 +00001634 channel_statistics[AllChannels].depth,(double)
1635 channel_statistics[i].depth);
1636 channel_statistics[AllChannels].minima=MagickMin(
1637 channel_statistics[AllChannels].minima,channel_statistics[i].minima);
1638 channel_statistics[AllChannels].maxima=MagickMax(
1639 channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
1640 channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
1641 channel_statistics[AllChannels].standard_deviation+=
1642 channel_statistics[i].standard_deviation;
1643 channel_statistics[AllChannels].kurtosis+=channel_statistics[i].kurtosis;
1644 channel_statistics[AllChannels].skewness+=channel_statistics[i].skewness;
1645 }
1646 channels=4;
1647 if (image->colorspace == CMYKColorspace)
1648 channels++;
1649 channel_statistics[AllChannels].mean/=channels;
1650 channel_statistics[AllChannels].standard_deviation/=channels;
1651 channel_statistics[AllChannels].kurtosis/=channels;
1652 channel_statistics[AllChannels].skewness/=channels;
1653 for (i=0; i <= AllChannels; i++)
1654 {
1655 sum_squares=0.0;
1656 sum_squares=channel_statistics[i].standard_deviation;
1657 sum_cubes=0.0;
1658 sum_cubes=channel_statistics[i].skewness;
1659 channel_statistics[i].standard_deviation=sqrt(
1660 channel_statistics[i].standard_deviation-
1661 (channel_statistics[i].mean*channel_statistics[i].mean));
1662 if (channel_statistics[i].standard_deviation == 0.0)
1663 {
1664 channel_statistics[i].kurtosis=0.0;
1665 channel_statistics[i].skewness=0.0;
1666 }
1667 else
1668 {
1669 channel_statistics[i].skewness=(channel_statistics[i].skewness-
1670 3.0*channel_statistics[i].mean*sum_squares+
1671 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1672 channel_statistics[i].mean)/
1673 (channel_statistics[i].standard_deviation*
1674 channel_statistics[i].standard_deviation*
1675 channel_statistics[i].standard_deviation);
1676 channel_statistics[i].kurtosis=(channel_statistics[i].kurtosis-
1677 4.0*channel_statistics[i].mean*sum_cubes+
1678 6.0*channel_statistics[i].mean*channel_statistics[i].mean*sum_squares-
1679 3.0*channel_statistics[i].mean*channel_statistics[i].mean*
1680 1.0*channel_statistics[i].mean*channel_statistics[i].mean)/
1681 (channel_statistics[i].standard_deviation*
1682 channel_statistics[i].standard_deviation*
1683 channel_statistics[i].standard_deviation*
1684 channel_statistics[i].standard_deviation)-3.0;
1685 }
1686 }
1687 return(channel_statistics);
1688}