blob: 16cbcd3bc4aa334be35fae1466be360068770c01 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% SSSSS TTTTT AAA TTTTT IIIII SSSSS TTTTT IIIII CCCC %
7% SS T A A T I SS T I C %
8% SSS T AAAAA T I SSS T I C %
9% SS T A A T I SS T I C %
10% SSSSS T A A T IIIII SSSSS T IIIII CCCC %
11% %
12% %
cristy3e2860c2010-01-24 01:36:30 +000013% MagickCore Image Statistical Methods %
cristy3ed852e2009-09-05 21:47:34 +000014% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/property.h"
45#include "magick/animate.h"
46#include "magick/blob.h"
47#include "magick/blob-private.h"
48#include "magick/cache.h"
49#include "magick/cache-private.h"
50#include "magick/cache-view.h"
51#include "magick/client.h"
52#include "magick/color.h"
53#include "magick/color-private.h"
54#include "magick/colorspace.h"
55#include "magick/colorspace-private.h"
56#include "magick/composite.h"
57#include "magick/composite-private.h"
58#include "magick/compress.h"
59#include "magick/constitute.h"
60#include "magick/deprecate.h"
61#include "magick/display.h"
62#include "magick/draw.h"
63#include "magick/enhance.h"
64#include "magick/exception.h"
65#include "magick/exception-private.h"
66#include "magick/gem.h"
67#include "magick/geometry.h"
68#include "magick/list.h"
69#include "magick/image-private.h"
70#include "magick/magic.h"
71#include "magick/magick.h"
72#include "magick/memory_.h"
73#include "magick/module.h"
74#include "magick/monitor.h"
cristyacd5a392009-09-18 00:04:22 +000075#include "magick/monitor-private.h"
cristy3ed852e2009-09-05 21:47:34 +000076#include "magick/option.h"
77#include "magick/paint.h"
78#include "magick/pixel-private.h"
79#include "magick/profile.h"
80#include "magick/quantize.h"
81#include "magick/random_.h"
cristy351842f2010-03-07 15:27:38 +000082#include "magick/random-private.h"
cristy3ed852e2009-09-05 21:47:34 +000083#include "magick/segment.h"
84#include "magick/semaphore.h"
85#include "magick/signature-private.h"
86#include "magick/statistic.h"
87#include "magick/string_.h"
88#include "magick/thread-private.h"
89#include "magick/timer.h"
90#include "magick/utility.h"
91#include "magick/version.h"
92
93/*
94%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95% %
96% %
97% %
cristyd18ae7c2010-03-07 17:39:52 +000098% E v a l u a t e I m a g e %
cristy316d5172009-09-17 19:31:25 +000099% %
100% %
101% %
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103%
cristyd18ae7c2010-03-07 17:39:52 +0000104% EvaluateImage() applies a value to the image with an arithmetic, relational,
105% or logical operator to an image. Use these operations to lighten or darken
106% an image, to increase or decrease contrast in an image, or to produce the
107% "negative" of an image.
cristy316d5172009-09-17 19:31:25 +0000108%
cristyd18ae7c2010-03-07 17:39:52 +0000109% The format of the EvaluateImageChannel method is:
cristy316d5172009-09-17 19:31:25 +0000110%
cristyd18ae7c2010-03-07 17:39:52 +0000111% MagickBooleanType EvaluateImage(Image *image,
112% const MagickEvaluateOperator op,const double value,
113% ExceptionInfo *exception)
114% MagickBooleanType EvaluateImages(Image *images,
115% const MagickEvaluateOperator op,const double value,
116% ExceptionInfo *exception)
117% MagickBooleanType EvaluateImageChannel(Image *image,
118% const ChannelType channel,const MagickEvaluateOperator op,
119% const double value,ExceptionInfo *exception)
cristy316d5172009-09-17 19:31:25 +0000120%
121% A description of each parameter follows:
122%
cristyd18ae7c2010-03-07 17:39:52 +0000123% o image: the image.
124%
125% o channel: the channel.
126%
127% o op: A channel op.
128%
129% o value: A value value.
cristy316d5172009-09-17 19:31:25 +0000130%
131% o exception: return any errors or warnings in this structure.
132%
133*/
134
135static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
136{
cristybb503372010-05-27 20:51:26 +0000137 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000138 i;
139
140 assert(pixels != (MagickPixelPacket **) NULL);
cristybb503372010-05-27 20:51:26 +0000141 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy316d5172009-09-17 19:31:25 +0000142 if (pixels[i] != (MagickPixelPacket *) NULL)
143 pixels[i]=(MagickPixelPacket *) RelinquishMagickMemory(pixels[i]);
cristyb41ee102010-10-04 16:46:15 +0000144 pixels=(MagickPixelPacket **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000145 return(pixels);
146}
147
cristy08a3d702010-11-28 01:57:36 +0000148static MagickPixelPacket **AcquirePixelThreadSet(const Image *image,
149 const size_t number_images)
cristy316d5172009-09-17 19:31:25 +0000150{
cristybb503372010-05-27 20:51:26 +0000151 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000152 i,
153 j;
154
155 MagickPixelPacket
156 **pixels;
157
cristybb503372010-05-27 20:51:26 +0000158 size_t
cristy08a3d702010-11-28 01:57:36 +0000159 length,
cristy316d5172009-09-17 19:31:25 +0000160 number_threads;
161
162 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +0000163 pixels=(MagickPixelPacket **) AcquireQuantumMemory(number_threads,
cristy316d5172009-09-17 19:31:25 +0000164 sizeof(*pixels));
165 if (pixels == (MagickPixelPacket **) NULL)
166 return((MagickPixelPacket **) NULL);
167 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000168 for (i=0; i < (ssize_t) number_threads; i++)
cristy316d5172009-09-17 19:31:25 +0000169 {
cristy08a3d702010-11-28 01:57:36 +0000170 length=image->columns;
171 if (length < number_images)
172 length=number_images;
173 pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(length,
cristy316d5172009-09-17 19:31:25 +0000174 sizeof(**pixels));
175 if (pixels[i] == (MagickPixelPacket *) NULL)
176 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000177 for (j=0; j < (ssize_t) length; j++)
cristy316d5172009-09-17 19:31:25 +0000178 GetMagickPixelPacket(image,&pixels[i][j]);
179 }
180 return(pixels);
181}
182
cristy351842f2010-03-07 15:27:38 +0000183static inline double MagickMax(const double x,const double y)
184{
185 if (x > y)
186 return(x);
187 return(y);
188}
189
cristy08a3d702010-11-28 01:57:36 +0000190#if defined(__cplusplus) || defined(c_plusplus)
191extern "C" {
192#endif
193
194static int IntensityCompare(const void *x,const void *y)
195{
196 const MagickPixelPacket
197 *color_1,
198 *color_2;
199
200 int
201 intensity;
202
203 color_1=(const MagickPixelPacket *) x;
204 color_2=(const MagickPixelPacket *) y;
205 intensity=(int) MagickPixelIntensity(color_2)-
206 (int) MagickPixelIntensity(color_1);
207 return(intensity);
208}
209
210#if defined(__cplusplus) || defined(c_plusplus)
211}
212#endif
213
cristy351842f2010-03-07 15:27:38 +0000214static inline double MagickMin(const double x,const double y)
215{
216 if (x < y)
217 return(x);
218 return(y);
219}
220
cristyd18ae7c2010-03-07 17:39:52 +0000221static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
222 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000223{
224 MagickRealType
225 result;
226
227 result=0.0;
228 switch (op)
229 {
230 case UndefinedEvaluateOperator:
231 break;
cristy33aaed22010-08-11 18:10:50 +0000232 case AbsEvaluateOperator:
233 {
234 result=(MagickRealType) fabs((double) (pixel+value));
235 break;
236 }
cristy351842f2010-03-07 15:27:38 +0000237 case AddEvaluateOperator:
238 {
239 result=(MagickRealType) (pixel+value);
240 break;
241 }
242 case AddModulusEvaluateOperator:
243 {
244 /*
245 This returns a 'floored modulus' of the addition which is a
246 positive result. It differs from % or fmod() which returns a
247 'truncated modulus' result, where floor() is replaced by trunc()
248 and could return a negative result (which is clipped).
249 */
250 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000251 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000252 break;
253 }
254 case AndEvaluateOperator:
255 {
cristy9fe8cd72010-10-19 01:24:07 +0000256 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000257 break;
258 }
259 case CosineEvaluateOperator:
260 {
261 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
262 QuantumScale*pixel*value))+0.5));
263 break;
264 }
265 case DivideEvaluateOperator:
266 {
267 result=pixel/(value == 0.0 ? 1.0 : value);
268 break;
269 }
cristy9fe8cd72010-10-19 01:24:07 +0000270 case ExponentialEvaluateOperator:
271 {
272 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
273 pixel)));
274 break;
275 }
cristy351842f2010-03-07 15:27:38 +0000276 case GaussianNoiseEvaluateOperator:
277 {
278 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
279 GaussianNoise,value);
280 break;
281 }
282 case ImpulseNoiseEvaluateOperator:
283 {
284 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
285 ImpulseNoise,value);
286 break;
287 }
288 case LaplacianNoiseEvaluateOperator:
289 {
290 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
291 LaplacianNoise,value);
292 break;
293 }
294 case LeftShiftEvaluateOperator:
295 {
cristy9fe8cd72010-10-19 01:24:07 +0000296 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000297 break;
298 }
299 case LogEvaluateOperator:
300 {
301 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
302 pixel+1.0))/log((double) (value+1.0)));
303 break;
304 }
305 case MaxEvaluateOperator:
306 {
307 result=(MagickRealType) MagickMax((double) pixel,value);
308 break;
309 }
310 case MeanEvaluateOperator:
311 {
cristy125a5a32010-05-07 13:30:52 +0000312 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000313 break;
314 }
cristy08a3d702010-11-28 01:57:36 +0000315 case MedianEvaluateOperator:
316 {
317 result=(MagickRealType) (pixel+value);
318 break;
319 }
cristy351842f2010-03-07 15:27:38 +0000320 case MinEvaluateOperator:
321 {
322 result=(MagickRealType) MagickMin((double) pixel,value);
323 break;
324 }
325 case MultiplicativeNoiseEvaluateOperator:
326 {
327 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
328 MultiplicativeGaussianNoise,value);
329 break;
330 }
331 case MultiplyEvaluateOperator:
332 {
333 result=(MagickRealType) (value*pixel);
334 break;
335 }
336 case OrEvaluateOperator:
337 {
cristy9fe8cd72010-10-19 01:24:07 +0000338 result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000339 break;
340 }
341 case PoissonNoiseEvaluateOperator:
342 {
343 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
344 PoissonNoise,value);
345 break;
346 }
347 case PowEvaluateOperator:
348 {
349 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
350 (double) value));
351 break;
352 }
353 case RightShiftEvaluateOperator:
354 {
cristy9fe8cd72010-10-19 01:24:07 +0000355 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000356 break;
357 }
358 case SetEvaluateOperator:
359 {
360 result=value;
361 break;
362 }
363 case SineEvaluateOperator:
364 {
365 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
366 QuantumScale*pixel*value))+0.5));
367 break;
368 }
369 case SubtractEvaluateOperator:
370 {
371 result=(MagickRealType) (pixel-value);
372 break;
373 }
374 case ThresholdEvaluateOperator:
375 {
376 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
377 QuantumRange);
378 break;
379 }
380 case ThresholdBlackEvaluateOperator:
381 {
382 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
383 break;
384 }
385 case ThresholdWhiteEvaluateOperator:
386 {
387 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
388 pixel);
389 break;
390 }
391 case UniformNoiseEvaluateOperator:
392 {
393 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
394 UniformNoise,value);
395 break;
396 }
397 case XorEvaluateOperator:
398 {
cristy9fe8cd72010-10-19 01:24:07 +0000399 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000400 break;
401 }
402 }
cristyd18ae7c2010-03-07 17:39:52 +0000403 return(result);
cristy351842f2010-03-07 15:27:38 +0000404}
405
406MagickExport MagickBooleanType EvaluateImage(Image *image,
407 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
408{
409 MagickBooleanType
410 status;
411
cristy9a9230e2011-04-26 14:56:14 +0000412 status=EvaluateImageChannel(image,CompositeChannels,op,value,exception);
cristy351842f2010-03-07 15:27:38 +0000413 return(status);
414}
415
cristyd18ae7c2010-03-07 17:39:52 +0000416MagickExport Image *EvaluateImages(const Image *images,
417 const MagickEvaluateOperator op,ExceptionInfo *exception)
418{
419#define EvaluateImageTag "Evaluate/Image"
420
421 CacheView
422 *evaluate_view;
423
424 const Image
425 *next;
426
427 Image
428 *evaluate_image;
429
cristyd18ae7c2010-03-07 17:39:52 +0000430 MagickBooleanType
431 status;
432
cristy5f959472010-05-27 22:19:46 +0000433 MagickOffsetType
434 progress;
435
cristyd18ae7c2010-03-07 17:39:52 +0000436 MagickPixelPacket
437 **restrict evaluate_pixels,
438 zero;
439
440 RandomInfo
441 **restrict random_info;
442
cristybb503372010-05-27 20:51:26 +0000443 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000444 number_images;
445
cristy5f959472010-05-27 22:19:46 +0000446 ssize_t
447 y;
448
cristyd18ae7c2010-03-07 17:39:52 +0000449 /*
450 Ensure the image are the same size.
451 */
452 assert(images != (Image *) NULL);
453 assert(images->signature == MagickSignature);
454 if (images->debug != MagickFalse)
455 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
456 assert(exception != (ExceptionInfo *) NULL);
457 assert(exception->signature == MagickSignature);
458 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
459 if ((next->columns != images->columns) || (next->rows != images->rows))
460 {
461 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
462 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
463 return((Image *) NULL);
464 }
465 /*
466 Initialize evaluate next attributes.
467 */
468 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
469 exception);
470 if (evaluate_image == (Image *) NULL)
471 return((Image *) NULL);
472 if (SetImageStorageClass(evaluate_image,DirectClass) == MagickFalse)
473 {
474 InheritException(exception,&evaluate_image->exception);
475 evaluate_image=DestroyImage(evaluate_image);
476 return((Image *) NULL);
477 }
cristy08a3d702010-11-28 01:57:36 +0000478 number_images=GetImageListLength(images);
479 evaluate_pixels=AcquirePixelThreadSet(images,number_images);
cristyd18ae7c2010-03-07 17:39:52 +0000480 if (evaluate_pixels == (MagickPixelPacket **) NULL)
481 {
482 evaluate_image=DestroyImage(evaluate_image);
483 (void) ThrowMagickException(exception,GetMagickModule(),
484 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
485 return((Image *) NULL);
486 }
487 /*
488 Evaluate image pixels.
489 */
490 status=MagickTrue;
491 progress=0;
492 GetMagickPixelPacket(images,&zero);
493 random_info=AcquireRandomInfoThreadSet();
cristyd18ae7c2010-03-07 17:39:52 +0000494 evaluate_view=AcquireCacheView(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000495 if (op == MedianEvaluateOperator)
cristyd18ae7c2010-03-07 17:39:52 +0000496#if defined(MAGICKCORE_OPENMP_SUPPORT)
497 #pragma omp parallel for schedule(dynamic) shared(progress,status)
498#endif
cristy08a3d702010-11-28 01:57:36 +0000499 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000500 {
cristy08a3d702010-11-28 01:57:36 +0000501 CacheView
502 *image_view;
cristyd18ae7c2010-03-07 17:39:52 +0000503
cristy08a3d702010-11-28 01:57:36 +0000504 const Image
505 *next;
cristyd18ae7c2010-03-07 17:39:52 +0000506
cristy08a3d702010-11-28 01:57:36 +0000507 const int
508 id = GetOpenMPThreadId();
509
cristy08a3d702010-11-28 01:57:36 +0000510 register IndexPacket
511 *restrict evaluate_indexes;
512
513 register MagickPixelPacket
514 *evaluate_pixel;
515
516 register PixelPacket
517 *restrict q;
518
519 register ssize_t
520 x;
521
522 if (status == MagickFalse)
523 continue;
524 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
525 1,exception);
526 if (q == (PixelPacket *) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000527 {
cristy08a3d702010-11-28 01:57:36 +0000528 status=MagickFalse;
529 continue;
cristyd18ae7c2010-03-07 17:39:52 +0000530 }
cristy08a3d702010-11-28 01:57:36 +0000531 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
cristy08a3d702010-11-28 01:57:36 +0000532 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000533 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000534 {
cristy08a3d702010-11-28 01:57:36 +0000535 register ssize_t
536 i;
537
538 for (i=0; i < (ssize_t) number_images; i++)
539 evaluate_pixel[i]=zero;
540 next=images;
541 for (i=0; i < (ssize_t) number_images; i++)
542 {
543 register const IndexPacket
544 *indexes;
545
546 register const PixelPacket
547 *p;
548
549 image_view=AcquireCacheView(next);
550 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
551 if (p == (const PixelPacket *) NULL)
552 {
553 image_view=DestroyCacheView(image_view);
554 break;
555 }
556 indexes=GetCacheViewVirtualIndexQueue(image_view);
557 evaluate_pixel[i].red=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000558 GetRedPixelComponent(p),op,evaluate_pixel[i].red);
cristy08a3d702010-11-28 01:57:36 +0000559 evaluate_pixel[i].green=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000560 GetGreenPixelComponent(p),op,evaluate_pixel[i].green);
cristy08a3d702010-11-28 01:57:36 +0000561 evaluate_pixel[i].blue=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000562 GetBluePixelComponent(p),op,evaluate_pixel[i].blue);
cristy08a3d702010-11-28 01:57:36 +0000563 evaluate_pixel[i].opacity=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000564 GetOpacityPixelComponent(p),op,evaluate_pixel[i].opacity);
cristy08a3d702010-11-28 01:57:36 +0000565 if (evaluate_image->colorspace == CMYKColorspace)
566 evaluate_pixel[i].index=ApplyEvaluateOperator(random_info[id],
567 *indexes,op,evaluate_pixel[i].index);
568 image_view=DestroyCacheView(image_view);
569 next=GetNextImageInList(next);
570 }
571 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
572 IntensityCompare);
573 q->red=ClampToQuantum(evaluate_pixel[i/2].red);
574 q->green=ClampToQuantum(evaluate_pixel[i/2].green);
575 q->blue=ClampToQuantum(evaluate_pixel[i/2].blue);
576 if (evaluate_image->matte == MagickFalse)
577 q->opacity=ClampToQuantum(evaluate_pixel[i/2].opacity);
578 else
579 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[i/2].opacity);
580 if (evaluate_image->colorspace == CMYKColorspace)
581 evaluate_indexes[i]=ClampToQuantum(evaluate_pixel[i/2].index);
582 q++;
cristy125a5a32010-05-07 13:30:52 +0000583 }
cristy08a3d702010-11-28 01:57:36 +0000584 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
585 status=MagickFalse;
586 if (images->progress_monitor != (MagickProgressMonitor) NULL)
587 {
588 MagickBooleanType
589 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000590
591#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000592 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000593#endif
cristy08a3d702010-11-28 01:57:36 +0000594 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
595 evaluate_image->rows);
596 if (proceed == MagickFalse)
597 status=MagickFalse;
598 }
599 }
600 else
601#if defined(MAGICKCORE_OPENMP_SUPPORT)
602 #pragma omp parallel for schedule(dynamic) shared(progress,status)
603#endif
604 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
605 {
606 CacheView
607 *image_view;
608
609 const Image
610 *next;
611
612 const int
613 id = GetOpenMPThreadId();
614
cristy08a3d702010-11-28 01:57:36 +0000615 register IndexPacket
616 *restrict evaluate_indexes;
617
618 register ssize_t
619 i,
620 x;
621
622 register MagickPixelPacket
623 *evaluate_pixel;
624
625 register PixelPacket
626 *restrict q;
627
628 if (status == MagickFalse)
629 continue;
630 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
631 1,exception);
632 if (q == (PixelPacket *) NULL)
633 {
cristyd18ae7c2010-03-07 17:39:52 +0000634 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000635 continue;
636 }
637 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
cristy08a3d702010-11-28 01:57:36 +0000638 evaluate_pixel=evaluate_pixels[id];
639 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
640 evaluate_pixel[x]=zero;
641 next=images;
642 for (i=0; i < (ssize_t) number_images; i++)
643 {
644 register const IndexPacket
645 *indexes;
646
647 register const PixelPacket
648 *p;
649
650 image_view=AcquireCacheView(next);
651 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
652 if (p == (const PixelPacket *) NULL)
653 {
654 image_view=DestroyCacheView(image_view);
655 break;
656 }
657 indexes=GetCacheViewVirtualIndexQueue(image_view);
658 for (x=0; x < (ssize_t) next->columns; x++)
659 {
660 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000661 GetRedPixelComponent(p),i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
cristy08a3d702010-11-28 01:57:36 +0000662 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000663 GetGreenPixelComponent(p),i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].green);
cristy08a3d702010-11-28 01:57:36 +0000664 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000665 GetBluePixelComponent(p),i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].blue);
cristy08a3d702010-11-28 01:57:36 +0000666 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000667 GetOpacityPixelComponent(p),i == 0 ? AddEvaluateOperator : op,
cristy08a3d702010-11-28 01:57:36 +0000668 evaluate_pixel[x].opacity);
669 if (evaluate_image->colorspace == CMYKColorspace)
670 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
671 indexes[x],i == 0 ? AddEvaluateOperator : op,
672 evaluate_pixel[x].index);
673 p++;
674 }
675 image_view=DestroyCacheView(image_view);
676 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000677 }
cristy08a3d702010-11-28 01:57:36 +0000678 if (op == MeanEvaluateOperator)
679 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
680 {
681 evaluate_pixel[x].red/=number_images;
682 evaluate_pixel[x].green/=number_images;
683 evaluate_pixel[x].blue/=number_images;
684 evaluate_pixel[x].opacity/=number_images;
685 evaluate_pixel[x].index/=number_images;
686 }
687 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
688 {
689 q->red=ClampToQuantum(evaluate_pixel[x].red);
690 q->green=ClampToQuantum(evaluate_pixel[x].green);
691 q->blue=ClampToQuantum(evaluate_pixel[x].blue);
692 if (evaluate_image->matte == MagickFalse)
693 q->opacity=ClampToQuantum(evaluate_pixel[x].opacity);
694 else
695 q->opacity=ClampToQuantum(QuantumRange-evaluate_pixel[x].opacity);
696 if (evaluate_image->colorspace == CMYKColorspace)
697 evaluate_indexes[x]=ClampToQuantum(evaluate_pixel[x].index);
698 q++;
699 }
700 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
701 status=MagickFalse;
702 if (images->progress_monitor != (MagickProgressMonitor) NULL)
703 {
704 MagickBooleanType
705 proceed;
706
707#if defined(MAGICKCORE_OPENMP_SUPPORT)
708 #pragma omp critical (MagickCore_EvaluateImages)
709#endif
710 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
711 evaluate_image->rows);
712 if (proceed == MagickFalse)
713 status=MagickFalse;
714 }
715 }
cristyd18ae7c2010-03-07 17:39:52 +0000716 evaluate_view=DestroyCacheView(evaluate_view);
717 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
718 random_info=DestroyRandomInfoThreadSet(random_info);
719 if (status == MagickFalse)
720 evaluate_image=DestroyImage(evaluate_image);
721 return(evaluate_image);
722}
723
cristy351842f2010-03-07 15:27:38 +0000724MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
725 const ChannelType channel,const MagickEvaluateOperator op,const double value,
726 ExceptionInfo *exception)
727{
cristy351842f2010-03-07 15:27:38 +0000728 CacheView
729 *image_view;
730
cristy351842f2010-03-07 15:27:38 +0000731 MagickBooleanType
732 status;
733
cristy5f959472010-05-27 22:19:46 +0000734 MagickOffsetType
735 progress;
736
cristy351842f2010-03-07 15:27:38 +0000737 RandomInfo
738 **restrict random_info;
739
cristy5f959472010-05-27 22:19:46 +0000740 ssize_t
741 y;
742
cristy351842f2010-03-07 15:27:38 +0000743 assert(image != (Image *) NULL);
744 assert(image->signature == MagickSignature);
745 if (image->debug != MagickFalse)
746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
747 assert(exception != (ExceptionInfo *) NULL);
748 assert(exception->signature == MagickSignature);
749 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
750 {
751 InheritException(exception,&image->exception);
752 return(MagickFalse);
753 }
754 status=MagickTrue;
755 progress=0;
756 random_info=AcquireRandomInfoThreadSet();
757 image_view=AcquireCacheView(image);
758#if defined(MAGICKCORE_OPENMP_SUPPORT)
759 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
760#endif
cristybb503372010-05-27 20:51:26 +0000761 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000762 {
cristy5c9e6f22010-09-17 17:31:01 +0000763 const int
764 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000765
cristy351842f2010-03-07 15:27:38 +0000766 register IndexPacket
767 *restrict indexes;
768
cristy351842f2010-03-07 15:27:38 +0000769 register PixelPacket
770 *restrict q;
771
cristy5c9e6f22010-09-17 17:31:01 +0000772 register ssize_t
773 x;
774
cristy351842f2010-03-07 15:27:38 +0000775 if (status == MagickFalse)
776 continue;
777 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
778 if (q == (PixelPacket *) NULL)
779 {
780 status=MagickFalse;
781 continue;
782 }
783 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000784 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000785 {
786 if ((channel & RedChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000787 q->red=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->red,op,
788 value));
cristy351842f2010-03-07 15:27:38 +0000789 if ((channel & GreenChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000790 q->green=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->green,
791 op,value));
cristy351842f2010-03-07 15:27:38 +0000792 if ((channel & BlueChannel) != 0)
cristyd18ae7c2010-03-07 17:39:52 +0000793 q->blue=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q->blue,op,
794 value));
cristy351842f2010-03-07 15:27:38 +0000795 if ((channel & OpacityChannel) != 0)
796 {
797 if (image->matte == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000798 q->opacity=ClampToQuantum(ApplyEvaluateOperator(random_info[id],
799 q->opacity,op,value));
cristy351842f2010-03-07 15:27:38 +0000800 else
cristyd18ae7c2010-03-07 17:39:52 +0000801 q->opacity=ClampToQuantum(QuantumRange-ApplyEvaluateOperator(
802 random_info[id],(Quantum) GetAlphaPixelComponent(q),op,value));
cristy351842f2010-03-07 15:27:38 +0000803 }
804 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyd18ae7c2010-03-07 17:39:52 +0000805 indexes[x]=(IndexPacket) ClampToQuantum(ApplyEvaluateOperator(
806 random_info[id],indexes[x],op,value));
cristy351842f2010-03-07 15:27:38 +0000807 q++;
808 }
809 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
810 status=MagickFalse;
811 if (image->progress_monitor != (MagickProgressMonitor) NULL)
812 {
813 MagickBooleanType
814 proceed;
815
816#if defined(MAGICKCORE_OPENMP_SUPPORT)
817 #pragma omp critical (MagickCore_EvaluateImageChannel)
818#endif
819 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
820 if (proceed == MagickFalse)
821 status=MagickFalse;
822 }
823 }
824 image_view=DestroyCacheView(image_view);
825 random_info=DestroyRandomInfoThreadSet(random_info);
826 return(status);
827}
828
829/*
830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831% %
832% %
833% %
834% F u n c t i o n I m a g e %
835% %
836% %
837% %
838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839%
840% FunctionImage() applies a value to the image with an arithmetic, relational,
841% or logical operator to an image. Use these operations to lighten or darken
842% an image, to increase or decrease contrast in an image, or to produce the
843% "negative" of an image.
844%
845% The format of the FunctionImageChannel method is:
846%
847% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000848% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000849% const double *parameters,ExceptionInfo *exception)
850% MagickBooleanType FunctionImageChannel(Image *image,
851% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000852% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000853% ExceptionInfo *exception)
854%
855% A description of each parameter follows:
856%
857% o image: the image.
858%
859% o channel: the channel.
860%
861% o function: A channel function.
862%
863% o parameters: one or more parameters.
864%
865% o exception: return any errors or warnings in this structure.
866%
867*/
868
869static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000870 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000871 ExceptionInfo *exception)
872{
873 MagickRealType
874 result;
875
cristybb503372010-05-27 20:51:26 +0000876 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000877 i;
878
879 (void) exception;
880 result=0.0;
881 switch (function)
882 {
883 case PolynomialFunction:
884 {
885 /*
886 * Polynomial
887 * Parameters: polynomial constants, highest to lowest order
888 * For example: c0*x^3 + c1*x^2 + c2*x + c3
889 */
890 result=0.0;
cristybb503372010-05-27 20:51:26 +0000891 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000892 result = result*QuantumScale*pixel + parameters[i];
893 result *= QuantumRange;
894 break;
895 }
896 case SinusoidFunction:
897 {
898 /* Sinusoid Function
899 * Parameters: Freq, Phase, Ampl, bias
900 */
901 double freq,phase,ampl,bias;
902 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
903 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
904 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
905 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
906 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
907 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
908 break;
909 }
910 case ArcsinFunction:
911 {
912 /* Arcsin Function (peged at range limits for invalid results)
913 * Parameters: Width, Center, Range, Bias
914 */
915 double width,range,center,bias;
916 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
917 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
918 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
919 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
920 result = 2.0/width*(QuantumScale*pixel - center);
921 if ( result <= -1.0 )
922 result = bias - range/2.0;
923 else if ( result >= 1.0 )
924 result = bias + range/2.0;
925 else
cristy7a07f9f2010-11-27 19:09:51 +0000926 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
cristy351842f2010-03-07 15:27:38 +0000927 result *= QuantumRange;
928 break;
929 }
930 case ArctanFunction:
931 {
932 /* Arctan Function
933 * Parameters: Slope, Center, Range, Bias
934 */
935 double slope,range,center,bias;
936 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
937 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
938 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
939 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000940 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000941 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
942 result) + bias ) );
943 break;
944 }
945 case UndefinedFunction:
946 break;
947 }
948 return(ClampToQuantum(result));
949}
950
951MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000952 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000953 const double *parameters,ExceptionInfo *exception)
954{
955 MagickBooleanType
956 status;
957
cristy9a9230e2011-04-26 14:56:14 +0000958 status=FunctionImageChannel(image,CompositeChannels,function,number_parameters,
cristy351842f2010-03-07 15:27:38 +0000959 parameters,exception);
960 return(status);
961}
962
963MagickExport MagickBooleanType FunctionImageChannel(Image *image,
964 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000965 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000966 ExceptionInfo *exception)
967{
968#define FunctionImageTag "Function/Image "
969
970 CacheView
971 *image_view;
972
cristy351842f2010-03-07 15:27:38 +0000973 MagickBooleanType
974 status;
975
cristy5f959472010-05-27 22:19:46 +0000976 MagickOffsetType
977 progress;
978
979 ssize_t
980 y;
981
cristy351842f2010-03-07 15:27:38 +0000982 assert(image != (Image *) NULL);
983 assert(image->signature == MagickSignature);
984 if (image->debug != MagickFalse)
985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
986 assert(exception != (ExceptionInfo *) NULL);
987 assert(exception->signature == MagickSignature);
988 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
989 {
990 InheritException(exception,&image->exception);
991 return(MagickFalse);
992 }
993 status=MagickTrue;
994 progress=0;
995 image_view=AcquireCacheView(image);
996#if defined(MAGICKCORE_OPENMP_SUPPORT)
997 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
998#endif
cristybb503372010-05-27 20:51:26 +0000999 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +00001000 {
1001 register IndexPacket
1002 *restrict indexes;
1003
cristybb503372010-05-27 20:51:26 +00001004 register ssize_t
cristy351842f2010-03-07 15:27:38 +00001005 x;
1006
1007 register PixelPacket
1008 *restrict q;
1009
1010 if (status == MagickFalse)
1011 continue;
1012 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1013 if (q == (PixelPacket *) NULL)
1014 {
1015 status=MagickFalse;
1016 continue;
1017 }
1018 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001019 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001020 {
1021 if ((channel & RedChannel) != 0)
1022 q->red=ApplyFunction(q->red,function,number_parameters,parameters,
1023 exception);
1024 if ((channel & GreenChannel) != 0)
1025 q->green=ApplyFunction(q->green,function,number_parameters,parameters,
1026 exception);
1027 if ((channel & BlueChannel) != 0)
1028 q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
1029 exception);
1030 if ((channel & OpacityChannel) != 0)
1031 {
1032 if (image->matte == MagickFalse)
1033 q->opacity=ApplyFunction(q->opacity,function,number_parameters,
1034 parameters,exception);
1035 else
1036 q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum)
1037 GetAlphaPixelComponent(q),function,number_parameters,parameters,
1038 exception);
1039 }
1040 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristy89bbeaf2011-04-22 20:25:27 +00001041 indexes[x]=(IndexPacket) ApplyFunction(GetIndexPixelComponent(indexes+x),function,
cristy351842f2010-03-07 15:27:38 +00001042 number_parameters,parameters,exception);
1043 q++;
1044 }
1045 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1046 status=MagickFalse;
1047 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1048 {
1049 MagickBooleanType
1050 proceed;
1051
1052#if defined(MAGICKCORE_OPENMP_SUPPORT)
1053 #pragma omp critical (MagickCore_FunctionImageChannel)
1054#endif
1055 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1056 if (proceed == MagickFalse)
1057 status=MagickFalse;
1058 }
1059 }
1060 image_view=DestroyCacheView(image_view);
1061 return(status);
1062}
1063
1064/*
1065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066% %
1067% %
1068% %
cristy3ed852e2009-09-05 21:47:34 +00001069+ G e t I m a g e C h a n n e l E x t r e m a %
1070% %
1071% %
1072% %
1073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074%
1075% GetImageChannelExtrema() returns the extrema of one or more image channels.
1076%
1077% The format of the GetImageChannelExtrema method is:
1078%
1079% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001080% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001081% ExceptionInfo *exception)
1082%
1083% A description of each parameter follows:
1084%
1085% o image: the image.
1086%
1087% o channel: the channel.
1088%
1089% o minima: the minimum value in the channel.
1090%
1091% o maxima: the maximum value in the channel.
1092%
1093% o exception: return any errors or warnings in this structure.
1094%
1095*/
1096
1097MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001098 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001099{
cristy9a9230e2011-04-26 14:56:14 +00001100 return(GetImageChannelExtrema(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001101}
1102
1103MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001104 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001105 ExceptionInfo *exception)
1106{
1107 double
1108 max,
1109 min;
1110
1111 MagickBooleanType
1112 status;
1113
1114 assert(image != (Image *) NULL);
1115 assert(image->signature == MagickSignature);
1116 if (image->debug != MagickFalse)
1117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1118 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001119 *minima=(size_t) ceil(min-0.5);
1120 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001121 return(status);
1122}
1123
1124/*
1125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126% %
1127% %
1128% %
1129% G e t I m a g e C h a n n e l M e a n %
1130% %
1131% %
1132% %
1133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1134%
1135% GetImageChannelMean() returns the mean and standard deviation of one or more
1136% image channels.
1137%
1138% The format of the GetImageChannelMean method is:
1139%
1140% MagickBooleanType GetImageChannelMean(const Image *image,
1141% const ChannelType channel,double *mean,double *standard_deviation,
1142% ExceptionInfo *exception)
1143%
1144% A description of each parameter follows:
1145%
1146% o image: the image.
1147%
1148% o channel: the channel.
1149%
1150% o mean: the average value in the channel.
1151%
1152% o standard_deviation: the standard deviation of the channel.
1153%
1154% o exception: return any errors or warnings in this structure.
1155%
1156*/
1157
1158MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1159 double *standard_deviation,ExceptionInfo *exception)
1160{
1161 MagickBooleanType
1162 status;
1163
cristy9a9230e2011-04-26 14:56:14 +00001164 status=GetImageChannelMean(image,CompositeChannels,mean,standard_deviation,
cristy3ed852e2009-09-05 21:47:34 +00001165 exception);
1166 return(status);
1167}
1168
1169MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1170 const ChannelType channel,double *mean,double *standard_deviation,
1171 ExceptionInfo *exception)
1172{
cristyfd9dcd42010-08-08 18:07:02 +00001173 ChannelStatistics
1174 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001175
cristyfd9dcd42010-08-08 18:07:02 +00001176 size_t
1177 channels;
cristy3ed852e2009-09-05 21:47:34 +00001178
1179 assert(image != (Image *) NULL);
1180 assert(image->signature == MagickSignature);
1181 if (image->debug != MagickFalse)
1182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001183 channel_statistics=GetImageChannelStatistics(image,exception);
1184 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001185 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001186 channels=0;
cristy9a9230e2011-04-26 14:56:14 +00001187 channel_statistics[CompositeChannels].mean=0.0;
1188 channel_statistics[CompositeChannels].standard_deviation=0.0;
cristyfd9dcd42010-08-08 18:07:02 +00001189 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001190 {
cristy9a9230e2011-04-26 14:56:14 +00001191 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001192 channel_statistics[RedChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001193 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001194 channel_statistics[RedChannel].variance-
1195 channel_statistics[RedChannel].mean*
1196 channel_statistics[RedChannel].mean;
1197 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001198 }
cristyfd9dcd42010-08-08 18:07:02 +00001199 if ((channel & GreenChannel) != 0)
1200 {
cristy9a9230e2011-04-26 14:56:14 +00001201 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001202 channel_statistics[GreenChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001203 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001204 channel_statistics[GreenChannel].variance-
1205 channel_statistics[GreenChannel].mean*
1206 channel_statistics[GreenChannel].mean;
1207 channels++;
1208 }
1209 if ((channel & BlueChannel) != 0)
1210 {
cristy9a9230e2011-04-26 14:56:14 +00001211 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001212 channel_statistics[BlueChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001213 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001214 channel_statistics[BlueChannel].variance-
1215 channel_statistics[BlueChannel].mean*
1216 channel_statistics[BlueChannel].mean;
1217 channels++;
1218 }
1219 if (((channel & OpacityChannel) != 0) &&
1220 (image->matte != MagickFalse))
1221 {
cristy9a9230e2011-04-26 14:56:14 +00001222 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001223 channel_statistics[OpacityChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001224 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001225 channel_statistics[OpacityChannel].variance-
1226 channel_statistics[OpacityChannel].mean*
1227 channel_statistics[OpacityChannel].mean;
1228 channels++;
1229 }
1230 if (((channel & IndexChannel) != 0) &&
1231 (image->colorspace == CMYKColorspace))
1232 {
cristy9a9230e2011-04-26 14:56:14 +00001233 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001234 channel_statistics[BlackChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001235 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001236 channel_statistics[BlackChannel].variance-
1237 channel_statistics[BlackChannel].mean*
1238 channel_statistics[BlackChannel].mean;
1239 channels++;
1240 }
cristy9a9230e2011-04-26 14:56:14 +00001241 channel_statistics[CompositeChannels].mean/=channels;
1242 channel_statistics[CompositeChannels].standard_deviation=
1243 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1244 *mean=channel_statistics[CompositeChannels].mean;
1245 *standard_deviation=channel_statistics[CompositeChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001246 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1247 channel_statistics);
1248 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001249}
1250
1251/*
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253% %
1254% %
1255% %
1256% G e t I m a g e C h a n n e l K u r t o s i s %
1257% %
1258% %
1259% %
1260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1261%
1262% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1263% image channels.
1264%
1265% The format of the GetImageChannelKurtosis method is:
1266%
1267% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1268% const ChannelType channel,double *kurtosis,double *skewness,
1269% ExceptionInfo *exception)
1270%
1271% A description of each parameter follows:
1272%
1273% o image: the image.
1274%
1275% o channel: the channel.
1276%
1277% o kurtosis: the kurtosis of the channel.
1278%
1279% o skewness: the skewness of the channel.
1280%
1281% o exception: return any errors or warnings in this structure.
1282%
1283*/
1284
1285MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1286 double *kurtosis,double *skewness,ExceptionInfo *exception)
1287{
1288 MagickBooleanType
1289 status;
1290
cristy9a9230e2011-04-26 14:56:14 +00001291 status=GetImageChannelKurtosis(image,CompositeChannels,kurtosis,skewness,
cristy3ed852e2009-09-05 21:47:34 +00001292 exception);
1293 return(status);
1294}
1295
1296MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1297 const ChannelType channel,double *kurtosis,double *skewness,
1298 ExceptionInfo *exception)
1299{
1300 double
1301 area,
1302 mean,
1303 standard_deviation,
1304 sum_squares,
1305 sum_cubes,
1306 sum_fourth_power;
1307
cristybb503372010-05-27 20:51:26 +00001308 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001309 y;
1310
1311 assert(image != (Image *) NULL);
1312 assert(image->signature == MagickSignature);
1313 if (image->debug != MagickFalse)
1314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1315 *kurtosis=0.0;
1316 *skewness=0.0;
1317 area=0.0;
1318 mean=0.0;
1319 standard_deviation=0.0;
1320 sum_squares=0.0;
1321 sum_cubes=0.0;
1322 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001323 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001324 {
1325 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001326 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001327
1328 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001329 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001330
cristybb503372010-05-27 20:51:26 +00001331 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001332 x;
1333
1334 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1335 if (p == (const PixelPacket *) NULL)
1336 break;
1337 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001338 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001339 {
1340 if ((channel & RedChannel) != 0)
1341 {
cristyce70c172010-01-07 17:15:30 +00001342 mean+=GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001343 sum_squares+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p);
1344 sum_cubes+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p)*GetRedPixelComponent(p);
1345 sum_fourth_power+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p)*GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001346 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001347 area++;
1348 }
1349 if ((channel & GreenChannel) != 0)
1350 {
cristyce70c172010-01-07 17:15:30 +00001351 mean+=GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001352 sum_squares+=(double) GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
1353 sum_cubes+=(double) GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
1354 sum_fourth_power+=(double) GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001355 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001356 area++;
1357 }
1358 if ((channel & BlueChannel) != 0)
1359 {
cristyce70c172010-01-07 17:15:30 +00001360 mean+=GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001361 sum_squares+=(double) GetBluePixelComponent(p)*GetBluePixelComponent(p);
1362 sum_cubes+=(double) GetBluePixelComponent(p)*GetBluePixelComponent(p)*GetBluePixelComponent(p);
1363 sum_fourth_power+=(double) GetBluePixelComponent(p)*GetBluePixelComponent(p)*GetBluePixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001364 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001365 area++;
1366 }
1367 if ((channel & OpacityChannel) != 0)
1368 {
cristyce70c172010-01-07 17:15:30 +00001369 mean+=GetOpacityPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001370 sum_squares+=(double) GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
1371 sum_cubes+=(double) GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
1372 sum_fourth_power+=(double) GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001373 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001374 area++;
1375 }
1376 if (((channel & IndexChannel) != 0) &&
1377 (image->colorspace == CMYKColorspace))
1378 {
1379 mean+=indexes[x];
1380 sum_squares+=(double) indexes[x]*indexes[x];
1381 sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
1382 sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
1383 indexes[x];
1384 area++;
1385 }
1386 p++;
1387 }
1388 }
cristybb503372010-05-27 20:51:26 +00001389 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001390 return(MagickFalse);
1391 if (area != 0.0)
1392 {
1393 mean/=area;
1394 sum_squares/=area;
1395 sum_cubes/=area;
1396 sum_fourth_power/=area;
1397 }
1398 standard_deviation=sqrt(sum_squares-(mean*mean));
1399 if (standard_deviation != 0.0)
1400 {
1401 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1402 3.0*mean*mean*mean*mean;
1403 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1404 standard_deviation;
1405 *kurtosis-=3.0;
1406 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1407 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1408 }
cristybb503372010-05-27 20:51:26 +00001409 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001410}
cristy46f08202010-01-10 04:04:21 +00001411
cristy3ed852e2009-09-05 21:47:34 +00001412/*
1413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1414% %
1415% %
1416% %
1417% G e t I m a g e C h a n n e l R a n g e %
1418% %
1419% %
1420% %
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422%
1423% GetImageChannelRange() returns the range of one or more image channels.
1424%
1425% The format of the GetImageChannelRange method is:
1426%
1427% MagickBooleanType GetImageChannelRange(const Image *image,
1428% const ChannelType channel,double *minima,double *maxima,
1429% ExceptionInfo *exception)
1430%
1431% A description of each parameter follows:
1432%
1433% o image: the image.
1434%
1435% o channel: the channel.
1436%
1437% o minima: the minimum value in the channel.
1438%
1439% o maxima: the maximum value in the channel.
1440%
1441% o exception: return any errors or warnings in this structure.
1442%
1443*/
1444
1445MagickExport MagickBooleanType GetImageRange(const Image *image,
1446 double *minima,double *maxima,ExceptionInfo *exception)
1447{
cristy9a9230e2011-04-26 14:56:14 +00001448 return(GetImageChannelRange(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001449}
1450
1451MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1452 const ChannelType channel,double *minima,double *maxima,
1453 ExceptionInfo *exception)
1454{
cristy3ed852e2009-09-05 21:47:34 +00001455 MagickPixelPacket
1456 pixel;
1457
cristy9d314ff2011-03-09 01:30:28 +00001458 ssize_t
1459 y;
1460
cristy3ed852e2009-09-05 21:47:34 +00001461 assert(image != (Image *) NULL);
1462 assert(image->signature == MagickSignature);
1463 if (image->debug != MagickFalse)
1464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1465 *maxima=(-1.0E-37);
1466 *minima=1.0E+37;
1467 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001469 {
1470 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001471 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001472
1473 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001474 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001475
cristybb503372010-05-27 20:51:26 +00001476 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001477 x;
1478
1479 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1480 if (p == (const PixelPacket *) NULL)
1481 break;
1482 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001483 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001484 {
1485 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1486 if ((channel & RedChannel) != 0)
1487 {
1488 if (pixel.red < *minima)
1489 *minima=(double) pixel.red;
1490 if (pixel.red > *maxima)
1491 *maxima=(double) pixel.red;
1492 }
1493 if ((channel & GreenChannel) != 0)
1494 {
1495 if (pixel.green < *minima)
1496 *minima=(double) pixel.green;
1497 if (pixel.green > *maxima)
1498 *maxima=(double) pixel.green;
1499 }
1500 if ((channel & BlueChannel) != 0)
1501 {
1502 if (pixel.blue < *minima)
1503 *minima=(double) pixel.blue;
1504 if (pixel.blue > *maxima)
1505 *maxima=(double) pixel.blue;
1506 }
1507 if ((channel & OpacityChannel) != 0)
1508 {
1509 if (pixel.opacity < *minima)
1510 *minima=(double) pixel.opacity;
1511 if (pixel.opacity > *maxima)
1512 *maxima=(double) pixel.opacity;
1513 }
1514 if (((channel & IndexChannel) != 0) &&
1515 (image->colorspace == CMYKColorspace))
1516 {
1517 if ((double) indexes[x] < *minima)
1518 *minima=(double) indexes[x];
1519 if ((double) indexes[x] > *maxima)
1520 *maxima=(double) indexes[x];
1521 }
1522 p++;
1523 }
1524 }
cristybb503372010-05-27 20:51:26 +00001525 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001526}
1527
1528/*
1529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1530% %
1531% %
1532% %
1533% 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 %
1534% %
1535% %
1536% %
1537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1538%
1539% GetImageChannelStatistics() returns statistics for each channel in the
1540% image. The statistics include the channel depth, its minima, maxima, mean,
1541% standard deviation, kurtosis and skewness. You can access the red channel
1542% mean, for example, like this:
1543%
cristy0c1c3fd2011-01-18 15:53:10 +00001544% channel_statistics=GetImageChannelStatistics(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001545% red_mean=channel_statistics[RedChannel].mean;
1546%
1547% Use MagickRelinquishMemory() to free the statistics buffer.
1548%
1549% The format of the GetImageChannelStatistics method is:
1550%
1551% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1552% ExceptionInfo *exception)
1553%
1554% A description of each parameter follows:
1555%
1556% o image: the image.
1557%
1558% o exception: return any errors or warnings in this structure.
1559%
1560*/
cristy3ed852e2009-09-05 21:47:34 +00001561MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1562 ExceptionInfo *exception)
1563{
1564 ChannelStatistics
1565 *channel_statistics;
1566
1567 double
cristyfd9dcd42010-08-08 18:07:02 +00001568 area;
cristy3ed852e2009-09-05 21:47:34 +00001569
cristy3ed852e2009-09-05 21:47:34 +00001570 MagickStatusType
1571 status;
1572
1573 QuantumAny
1574 range;
1575
cristybb503372010-05-27 20:51:26 +00001576 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 size_t
cristy9d314ff2011-03-09 01:30:28 +00001580 channels,
1581 depth,
cristy3ed852e2009-09-05 21:47:34 +00001582 length;
1583
cristy9d314ff2011-03-09 01:30:28 +00001584 ssize_t
1585 y;
cristy3ed852e2009-09-05 21:47:34 +00001586
1587 assert(image != (Image *) NULL);
1588 assert(image->signature == MagickSignature);
1589 if (image->debug != MagickFalse)
1590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy9a9230e2011-04-26 14:56:14 +00001591 length=CompositeChannels+1UL;
cristy3ed852e2009-09-05 21:47:34 +00001592 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1593 sizeof(*channel_statistics));
1594 if (channel_statistics == (ChannelStatistics *) NULL)
1595 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1596 (void) ResetMagickMemory(channel_statistics,0,length*
1597 sizeof(*channel_statistics));
cristy9a9230e2011-04-26 14:56:14 +00001598 for (i=0; i <= CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001599 {
1600 channel_statistics[i].depth=1;
1601 channel_statistics[i].maxima=(-1.0E-37);
1602 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001603 }
cristybb503372010-05-27 20:51:26 +00001604 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001605 {
1606 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001607 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001608
1609 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001610 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001611
cristybb503372010-05-27 20:51:26 +00001612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001613 x;
1614
1615 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1616 if (p == (const PixelPacket *) NULL)
1617 break;
1618 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001619 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001620 {
1621 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1622 {
1623 depth=channel_statistics[RedChannel].depth;
1624 range=GetQuantumRange(depth);
cristy89bbeaf2011-04-22 20:25:27 +00001625 status=GetRedPixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetRedPixelComponent(p),range),
cristy3ed852e2009-09-05 21:47:34 +00001626 range) ? MagickTrue : MagickFalse;
1627 if (status != MagickFalse)
1628 {
1629 channel_statistics[RedChannel].depth++;
1630 continue;
1631 }
1632 }
1633 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1634 {
1635 depth=channel_statistics[GreenChannel].depth;
1636 range=GetQuantumRange(depth);
cristy89bbeaf2011-04-22 20:25:27 +00001637 status=GetGreenPixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetGreenPixelComponent(p),
cristy3ed852e2009-09-05 21:47:34 +00001638 range),range) ? MagickTrue : MagickFalse;
1639 if (status != MagickFalse)
1640 {
1641 channel_statistics[GreenChannel].depth++;
1642 continue;
1643 }
1644 }
1645 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1646 {
1647 depth=channel_statistics[BlueChannel].depth;
1648 range=GetQuantumRange(depth);
cristy89bbeaf2011-04-22 20:25:27 +00001649 status=GetBluePixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetBluePixelComponent(p),
cristy3ed852e2009-09-05 21:47:34 +00001650 range),range) ? MagickTrue : MagickFalse;
1651 if (status != MagickFalse)
1652 {
1653 channel_statistics[BlueChannel].depth++;
1654 continue;
1655 }
1656 }
1657 if (image->matte != MagickFalse)
1658 {
1659 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1660 {
1661 depth=channel_statistics[OpacityChannel].depth;
1662 range=GetQuantumRange(depth);
cristy89bbeaf2011-04-22 20:25:27 +00001663 status=GetOpacityPixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(
cristyd05ecd12011-04-22 20:44:42 +00001664 GetOpacityPixelComponent(p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001665 if (status != MagickFalse)
1666 {
1667 channel_statistics[OpacityChannel].depth++;
1668 continue;
1669 }
1670 }
1671 }
1672 if (image->colorspace == CMYKColorspace)
1673 {
1674 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1675 {
1676 depth=channel_statistics[BlackChannel].depth;
1677 range=GetQuantumRange(depth);
1678 status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
1679 indexes[x],range),range) ? MagickTrue : MagickFalse;
1680 if (status != MagickFalse)
1681 {
1682 channel_statistics[BlackChannel].depth++;
1683 continue;
1684 }
1685 }
1686 }
cristyd05ecd12011-04-22 20:44:42 +00001687 if ((double) GetRedPixelComponent(p) < channel_statistics[RedChannel].minima)
cristyce70c172010-01-07 17:15:30 +00001688 channel_statistics[RedChannel].minima=(double) GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001689 if ((double) GetRedPixelComponent(p) > channel_statistics[RedChannel].maxima)
cristyce70c172010-01-07 17:15:30 +00001690 channel_statistics[RedChannel].maxima=(double) GetRedPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001691 channel_statistics[RedChannel].sum+=GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001692 channel_statistics[RedChannel].sum_squared+=(double) GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001693 GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001694 channel_statistics[RedChannel].sum_cubed+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001695 GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001696 channel_statistics[RedChannel].sum_fourth_power+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p)*
1697 GetRedPixelComponent(p)*GetRedPixelComponent(p);
1698 if ((double) GetGreenPixelComponent(p) < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001699 channel_statistics[GreenChannel].minima=(double)
1700 GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001701 if ((double) GetGreenPixelComponent(p) > channel_statistics[GreenChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001702 channel_statistics[GreenChannel].maxima=(double)
1703 GetGreenPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001704 channel_statistics[GreenChannel].sum+=GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001705 channel_statistics[GreenChannel].sum_squared+=(double) GetGreenPixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001706 GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001707 channel_statistics[GreenChannel].sum_cubed+=(double) GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001708 GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001709 channel_statistics[GreenChannel].sum_fourth_power+=(double) GetGreenPixelComponent(p)*
1710 GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
1711 if ((double) GetBluePixelComponent(p) < channel_statistics[BlueChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001712 channel_statistics[BlueChannel].minima=(double)
1713 GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001714 if ((double) GetBluePixelComponent(p) > channel_statistics[BlueChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001715 channel_statistics[BlueChannel].maxima=(double)
1716 GetBluePixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001717 channel_statistics[BlueChannel].sum+=GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001718 channel_statistics[BlueChannel].sum_squared+=(double) GetBluePixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001719 GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001720 channel_statistics[BlueChannel].sum_cubed+=(double) GetBluePixelComponent(p)*GetBluePixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001721 GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001722 channel_statistics[BlueChannel].sum_fourth_power+=(double) GetBluePixelComponent(p)*
1723 GetBluePixelComponent(p)*GetBluePixelComponent(p)*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001724 if (image->matte != MagickFalse)
1725 {
cristyd05ecd12011-04-22 20:44:42 +00001726 if ((double) GetOpacityPixelComponent(p) < channel_statistics[OpacityChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001727 channel_statistics[OpacityChannel].minima=(double)
1728 GetOpacityPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001729 if ((double) GetOpacityPixelComponent(p) > channel_statistics[OpacityChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001730 channel_statistics[OpacityChannel].maxima=(double)
1731 GetOpacityPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001732 channel_statistics[OpacityChannel].sum+=GetOpacityPixelComponent(p);
1733 channel_statistics[OpacityChannel].sum_squared+=(double)
cristyd05ecd12011-04-22 20:44:42 +00001734 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
1735 channel_statistics[OpacityChannel].sum_cubed+=(double) GetOpacityPixelComponent(p)*
1736 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001737 channel_statistics[OpacityChannel].sum_fourth_power+=(double)
cristyd05ecd12011-04-22 20:44:42 +00001738 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001739 }
1740 if (image->colorspace == CMYKColorspace)
1741 {
1742 if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
1743 channel_statistics[BlackChannel].minima=(double) indexes[x];
1744 if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
1745 channel_statistics[BlackChannel].maxima=(double) indexes[x];
cristyfd9dcd42010-08-08 18:07:02 +00001746 channel_statistics[BlackChannel].sum+=indexes[x];
1747 channel_statistics[BlackChannel].sum_squared+=(double)
cristy3ed852e2009-09-05 21:47:34 +00001748 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001749 channel_statistics[BlackChannel].sum_cubed+=(double) indexes[x]*
cristy3ed852e2009-09-05 21:47:34 +00001750 indexes[x]*indexes[x];
cristya8178ed2010-08-10 17:31:59 +00001751 channel_statistics[BlackChannel].sum_fourth_power+=(double)
1752 indexes[x]*indexes[x]*indexes[x]*indexes[x];
cristy3ed852e2009-09-05 21:47:34 +00001753 }
1754 x++;
1755 p++;
1756 }
1757 }
1758 area=(double) image->columns*image->rows;
cristy9a9230e2011-04-26 14:56:14 +00001759 for (i=0; i < CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001760 {
cristyfd9dcd42010-08-08 18:07:02 +00001761 channel_statistics[i].sum/=area;
1762 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001763 channel_statistics[i].sum_cubed/=area;
1764 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001765 channel_statistics[i].mean=channel_statistics[i].sum;
1766 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1767 channel_statistics[i].standard_deviation=sqrt(
1768 channel_statistics[i].variance-(channel_statistics[i].mean*
1769 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001770 }
cristy9a9230e2011-04-26 14:56:14 +00001771 for (i=0; i < CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001772 {
cristy9a9230e2011-04-26 14:56:14 +00001773 channel_statistics[CompositeChannels].depth=(size_t) MagickMax((double)
1774 channel_statistics[CompositeChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001775 channel_statistics[i].depth);
cristy9a9230e2011-04-26 14:56:14 +00001776 channel_statistics[CompositeChannels].minima=MagickMin(
1777 channel_statistics[CompositeChannels].minima,channel_statistics[i].minima);
1778 channel_statistics[CompositeChannels].maxima=MagickMax(
1779 channel_statistics[CompositeChannels].maxima,channel_statistics[i].maxima);
1780 channel_statistics[CompositeChannels].sum+=channel_statistics[i].sum;
1781 channel_statistics[CompositeChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001782 channel_statistics[i].sum_squared;
cristy9a9230e2011-04-26 14:56:14 +00001783 channel_statistics[CompositeChannels].sum_cubed+=channel_statistics[i].sum_cubed;
1784 channel_statistics[CompositeChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001785 channel_statistics[i].sum_fourth_power;
cristy9a9230e2011-04-26 14:56:14 +00001786 channel_statistics[CompositeChannels].mean+=channel_statistics[i].mean;
1787 channel_statistics[CompositeChannels].variance+=channel_statistics[i].variance-
cristyfd9dcd42010-08-08 18:07:02 +00001788 channel_statistics[i].mean*channel_statistics[i].mean;
cristy9a9230e2011-04-26 14:56:14 +00001789 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001790 channel_statistics[i].variance-channel_statistics[i].mean*
1791 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001792 }
cristycf584452010-08-08 02:26:04 +00001793 channels=3;
1794 if (image->matte != MagickFalse)
1795 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001796 if (image->colorspace == CMYKColorspace)
1797 channels++;
cristy9a9230e2011-04-26 14:56:14 +00001798 channel_statistics[CompositeChannels].sum/=channels;
1799 channel_statistics[CompositeChannels].sum_squared/=channels;
1800 channel_statistics[CompositeChannels].sum_cubed/=channels;
1801 channel_statistics[CompositeChannels].sum_fourth_power/=channels;
1802 channel_statistics[CompositeChannels].mean/=channels;
1803 channel_statistics[CompositeChannels].variance/=channels;
1804 channel_statistics[CompositeChannels].standard_deviation=
1805 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1806 channel_statistics[CompositeChannels].kurtosis/=channels;
1807 channel_statistics[CompositeChannels].skewness/=channels;
1808 for (i=0; i <= CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001809 {
cristy3ed852e2009-09-05 21:47:34 +00001810 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001811 continue;
1812 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1813 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1814 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1815 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1816 channel_statistics[i].standard_deviation*
1817 channel_statistics[i].standard_deviation);
1818 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1819 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1820 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1821 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1822 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1823 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1824 channel_statistics[i].standard_deviation*
1825 channel_statistics[i].standard_deviation*
1826 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001827 }
1828 return(channel_statistics);
1829}