blob: 0047aa5be0e5f9538f7fbfa39c5a0260c40db5ad [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);
cristyfba5a8b2011-05-03 17:12:12 +0000573 SetRedPixelComponent(q,ClampToQuantum(evaluate_pixel[i/2].red));
574 SetGreenPixelComponent(q,ClampToQuantum(evaluate_pixel[i/2].green));
575 SetBluePixelComponent(q,ClampToQuantum(evaluate_pixel[i/2].blue));
cristy08a3d702010-11-28 01:57:36 +0000576 if (evaluate_image->matte == MagickFalse)
cristyfba5a8b2011-05-03 17:12:12 +0000577 SetOpacityPixelComponent(q,ClampToQuantum(
578 evaluate_pixel[i/2].opacity));
cristy08a3d702010-11-28 01:57:36 +0000579 else
cristyfba5a8b2011-05-03 17:12:12 +0000580 SetOpacityPixelComponent(q,ClampToQuantum(QuantumRange-
581 evaluate_pixel[i/2].opacity));
cristy08a3d702010-11-28 01:57:36 +0000582 if (evaluate_image->colorspace == CMYKColorspace)
cristyfba5a8b2011-05-03 17:12:12 +0000583 SetIndexPixelComponent(evaluate_indexes+i,ClampToQuantum(
584 evaluate_pixel[i/2].index));
cristy08a3d702010-11-28 01:57:36 +0000585 q++;
cristy125a5a32010-05-07 13:30:52 +0000586 }
cristy08a3d702010-11-28 01:57:36 +0000587 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
588 status=MagickFalse;
589 if (images->progress_monitor != (MagickProgressMonitor) NULL)
590 {
591 MagickBooleanType
592 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000593
594#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000595 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000596#endif
cristy08a3d702010-11-28 01:57:36 +0000597 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
598 evaluate_image->rows);
599 if (proceed == MagickFalse)
600 status=MagickFalse;
601 }
602 }
603 else
604#if defined(MAGICKCORE_OPENMP_SUPPORT)
605 #pragma omp parallel for schedule(dynamic) shared(progress,status)
606#endif
607 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
608 {
609 CacheView
610 *image_view;
611
612 const Image
613 *next;
614
615 const int
616 id = GetOpenMPThreadId();
617
cristy08a3d702010-11-28 01:57:36 +0000618 register IndexPacket
619 *restrict evaluate_indexes;
620
621 register ssize_t
622 i,
623 x;
624
625 register MagickPixelPacket
626 *evaluate_pixel;
627
628 register PixelPacket
629 *restrict q;
630
631 if (status == MagickFalse)
632 continue;
633 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
634 1,exception);
635 if (q == (PixelPacket *) NULL)
636 {
cristyd18ae7c2010-03-07 17:39:52 +0000637 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000638 continue;
639 }
640 evaluate_indexes=GetCacheViewAuthenticIndexQueue(evaluate_view);
cristy08a3d702010-11-28 01:57:36 +0000641 evaluate_pixel=evaluate_pixels[id];
642 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
643 evaluate_pixel[x]=zero;
644 next=images;
645 for (i=0; i < (ssize_t) number_images; i++)
646 {
647 register const IndexPacket
648 *indexes;
649
650 register const PixelPacket
651 *p;
652
653 image_view=AcquireCacheView(next);
654 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
655 if (p == (const PixelPacket *) NULL)
656 {
657 image_view=DestroyCacheView(image_view);
658 break;
659 }
660 indexes=GetCacheViewVirtualIndexQueue(image_view);
661 for (x=0; x < (ssize_t) next->columns; x++)
662 {
663 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000664 GetRedPixelComponent(p),i == 0 ? AddEvaluateOperator : op,evaluate_pixel[x].red);
cristy08a3d702010-11-28 01:57:36 +0000665 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],
cristyfba5a8b2011-05-03 17:12:12 +0000666 GetGreenPixelComponent(p),i == 0 ? AddEvaluateOperator : op,
667 evaluate_pixel[x].green);
cristy08a3d702010-11-28 01:57:36 +0000668 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],
cristyfba5a8b2011-05-03 17:12:12 +0000669 GetBluePixelComponent(p),i == 0 ? AddEvaluateOperator : op,
670 evaluate_pixel[x].blue);
cristy08a3d702010-11-28 01:57:36 +0000671 evaluate_pixel[x].opacity=ApplyEvaluateOperator(random_info[id],
cristyd05ecd12011-04-22 20:44:42 +0000672 GetOpacityPixelComponent(p),i == 0 ? AddEvaluateOperator : op,
cristy08a3d702010-11-28 01:57:36 +0000673 evaluate_pixel[x].opacity);
674 if (evaluate_image->colorspace == CMYKColorspace)
675 evaluate_pixel[x].index=ApplyEvaluateOperator(random_info[id],
cristyfba5a8b2011-05-03 17:12:12 +0000676 GetIndexPixelComponent(indexes+x),i == 0 ? AddEvaluateOperator :
677 op,evaluate_pixel[x].index);
cristy08a3d702010-11-28 01:57:36 +0000678 p++;
679 }
680 image_view=DestroyCacheView(image_view);
681 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000682 }
cristy08a3d702010-11-28 01:57:36 +0000683 if (op == MeanEvaluateOperator)
684 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
685 {
686 evaluate_pixel[x].red/=number_images;
687 evaluate_pixel[x].green/=number_images;
688 evaluate_pixel[x].blue/=number_images;
689 evaluate_pixel[x].opacity/=number_images;
690 evaluate_pixel[x].index/=number_images;
691 }
692 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
693 {
cristyfba5a8b2011-05-03 17:12:12 +0000694 SetRedPixelComponent(q,ClampToQuantum(evaluate_pixel[x].red));
695 SetGreenPixelComponent(q,ClampToQuantum(evaluate_pixel[x].green));
696 SetBluePixelComponent(q,ClampToQuantum(evaluate_pixel[x].blue));
cristy08a3d702010-11-28 01:57:36 +0000697 if (evaluate_image->matte == MagickFalse)
cristyfba5a8b2011-05-03 17:12:12 +0000698 SetOpacityPixelComponent(q,ClampToQuantum(evaluate_pixel[x].opacity));
cristy08a3d702010-11-28 01:57:36 +0000699 else
cristyfba5a8b2011-05-03 17:12:12 +0000700 SetOpacityPixelComponent(q,ClampToQuantum(QuantumRange-
701 evaluate_pixel[x].opacity));
cristy08a3d702010-11-28 01:57:36 +0000702 if (evaluate_image->colorspace == CMYKColorspace)
cristyfba5a8b2011-05-03 17:12:12 +0000703 SetIndexPixelComponent(evaluate_indexes+x,ClampToQuantum(
704 evaluate_pixel[x].index));
cristy08a3d702010-11-28 01:57:36 +0000705 q++;
706 }
707 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
708 status=MagickFalse;
709 if (images->progress_monitor != (MagickProgressMonitor) NULL)
710 {
711 MagickBooleanType
712 proceed;
713
714#if defined(MAGICKCORE_OPENMP_SUPPORT)
715 #pragma omp critical (MagickCore_EvaluateImages)
716#endif
717 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
718 evaluate_image->rows);
719 if (proceed == MagickFalse)
720 status=MagickFalse;
721 }
722 }
cristyd18ae7c2010-03-07 17:39:52 +0000723 evaluate_view=DestroyCacheView(evaluate_view);
724 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
725 random_info=DestroyRandomInfoThreadSet(random_info);
726 if (status == MagickFalse)
727 evaluate_image=DestroyImage(evaluate_image);
728 return(evaluate_image);
729}
730
cristy351842f2010-03-07 15:27:38 +0000731MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
732 const ChannelType channel,const MagickEvaluateOperator op,const double value,
733 ExceptionInfo *exception)
734{
cristy351842f2010-03-07 15:27:38 +0000735 CacheView
736 *image_view;
737
cristy351842f2010-03-07 15:27:38 +0000738 MagickBooleanType
739 status;
740
cristy5f959472010-05-27 22:19:46 +0000741 MagickOffsetType
742 progress;
743
cristy351842f2010-03-07 15:27:38 +0000744 RandomInfo
745 **restrict random_info;
746
cristy5f959472010-05-27 22:19:46 +0000747 ssize_t
748 y;
749
cristy351842f2010-03-07 15:27:38 +0000750 assert(image != (Image *) NULL);
751 assert(image->signature == MagickSignature);
752 if (image->debug != MagickFalse)
753 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
754 assert(exception != (ExceptionInfo *) NULL);
755 assert(exception->signature == MagickSignature);
756 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
757 {
758 InheritException(exception,&image->exception);
759 return(MagickFalse);
760 }
761 status=MagickTrue;
762 progress=0;
763 random_info=AcquireRandomInfoThreadSet();
764 image_view=AcquireCacheView(image);
765#if defined(MAGICKCORE_OPENMP_SUPPORT)
766 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
767#endif
cristybb503372010-05-27 20:51:26 +0000768 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000769 {
cristy5c9e6f22010-09-17 17:31:01 +0000770 const int
771 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000772
cristy351842f2010-03-07 15:27:38 +0000773 register IndexPacket
774 *restrict indexes;
775
cristy351842f2010-03-07 15:27:38 +0000776 register PixelPacket
777 *restrict q;
778
cristy5c9e6f22010-09-17 17:31:01 +0000779 register ssize_t
780 x;
781
cristy351842f2010-03-07 15:27:38 +0000782 if (status == MagickFalse)
783 continue;
784 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
785 if (q == (PixelPacket *) NULL)
786 {
787 status=MagickFalse;
788 continue;
789 }
790 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000791 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000792 {
793 if ((channel & RedChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +0000794 SetRedPixelComponent(q,ClampToQuantum(ApplyEvaluateOperator(
795 random_info[id],GetRedPixelComponent(q),op,value)));
cristy351842f2010-03-07 15:27:38 +0000796 if ((channel & GreenChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +0000797 SetGreenPixelComponent(q,ClampToQuantum(ApplyEvaluateOperator(
798 random_info[id],GetGreenPixelComponent(q),op,value)));
cristy351842f2010-03-07 15:27:38 +0000799 if ((channel & BlueChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +0000800 SetBluePixelComponent(q,ClampToQuantum(ApplyEvaluateOperator(
801 random_info[id],GetBluePixelComponent(q),op,value)));
cristy351842f2010-03-07 15:27:38 +0000802 if ((channel & OpacityChannel) != 0)
803 {
804 if (image->matte == MagickFalse)
cristyfba5a8b2011-05-03 17:12:12 +0000805 SetOpacityPixelComponent(q,ClampToQuantum(ApplyEvaluateOperator(
806 random_info[id],GetOpacityPixelComponent(q),op,value)));
cristy351842f2010-03-07 15:27:38 +0000807 else
cristyfba5a8b2011-05-03 17:12:12 +0000808 SetOpacityPixelComponent(q,ClampToQuantum(QuantumRange-
809 ApplyEvaluateOperator(random_info[id],GetAlphaPixelComponent(q),
810 op,value)));
cristy351842f2010-03-07 15:27:38 +0000811 }
812 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyfba5a8b2011-05-03 17:12:12 +0000813 SetIndexPixelComponent(indexes+x,ClampToQuantum(ApplyEvaluateOperator(
814 random_info[id],GetIndexPixelComponent(indexes+x),op,value)));
cristy351842f2010-03-07 15:27:38 +0000815 q++;
816 }
817 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
818 status=MagickFalse;
819 if (image->progress_monitor != (MagickProgressMonitor) NULL)
820 {
821 MagickBooleanType
822 proceed;
823
824#if defined(MAGICKCORE_OPENMP_SUPPORT)
825 #pragma omp critical (MagickCore_EvaluateImageChannel)
826#endif
827 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
828 if (proceed == MagickFalse)
829 status=MagickFalse;
830 }
831 }
832 image_view=DestroyCacheView(image_view);
833 random_info=DestroyRandomInfoThreadSet(random_info);
834 return(status);
835}
836
837/*
838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839% %
840% %
841% %
842% F u n c t i o n I m a g e %
843% %
844% %
845% %
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847%
848% FunctionImage() applies a value to the image with an arithmetic, relational,
849% or logical operator to an image. Use these operations to lighten or darken
850% an image, to increase or decrease contrast in an image, or to produce the
851% "negative" of an image.
852%
853% The format of the FunctionImageChannel method is:
854%
855% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000856% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000857% const double *parameters,ExceptionInfo *exception)
858% MagickBooleanType FunctionImageChannel(Image *image,
859% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000860% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000861% ExceptionInfo *exception)
862%
863% A description of each parameter follows:
864%
865% o image: the image.
866%
867% o channel: the channel.
868%
869% o function: A channel function.
870%
871% o parameters: one or more parameters.
872%
873% o exception: return any errors or warnings in this structure.
874%
875*/
876
877static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000878 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000879 ExceptionInfo *exception)
880{
881 MagickRealType
882 result;
883
cristybb503372010-05-27 20:51:26 +0000884 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000885 i;
886
887 (void) exception;
888 result=0.0;
889 switch (function)
890 {
891 case PolynomialFunction:
892 {
893 /*
894 * Polynomial
895 * Parameters: polynomial constants, highest to lowest order
896 * For example: c0*x^3 + c1*x^2 + c2*x + c3
897 */
898 result=0.0;
cristybb503372010-05-27 20:51:26 +0000899 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000900 result = result*QuantumScale*pixel + parameters[i];
901 result *= QuantumRange;
902 break;
903 }
904 case SinusoidFunction:
905 {
906 /* Sinusoid Function
907 * Parameters: Freq, Phase, Ampl, bias
908 */
909 double freq,phase,ampl,bias;
910 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
911 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
912 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
913 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
914 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
915 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
916 break;
917 }
918 case ArcsinFunction:
919 {
920 /* Arcsin Function (peged at range limits for invalid results)
921 * Parameters: Width, Center, Range, Bias
922 */
923 double width,range,center,bias;
924 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
925 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
926 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
927 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
928 result = 2.0/width*(QuantumScale*pixel - center);
929 if ( result <= -1.0 )
930 result = bias - range/2.0;
931 else if ( result >= 1.0 )
932 result = bias + range/2.0;
933 else
cristy7a07f9f2010-11-27 19:09:51 +0000934 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
cristy351842f2010-03-07 15:27:38 +0000935 result *= QuantumRange;
936 break;
937 }
938 case ArctanFunction:
939 {
940 /* Arctan Function
941 * Parameters: Slope, Center, Range, Bias
942 */
943 double slope,range,center,bias;
944 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
945 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
946 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
947 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000948 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000949 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
950 result) + bias ) );
951 break;
952 }
953 case UndefinedFunction:
954 break;
955 }
956 return(ClampToQuantum(result));
957}
958
959MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000960 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000961 const double *parameters,ExceptionInfo *exception)
962{
963 MagickBooleanType
964 status;
965
cristy9a9230e2011-04-26 14:56:14 +0000966 status=FunctionImageChannel(image,CompositeChannels,function,number_parameters,
cristy351842f2010-03-07 15:27:38 +0000967 parameters,exception);
968 return(status);
969}
970
971MagickExport MagickBooleanType FunctionImageChannel(Image *image,
972 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000973 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000974 ExceptionInfo *exception)
975{
976#define FunctionImageTag "Function/Image "
977
978 CacheView
979 *image_view;
980
cristy351842f2010-03-07 15:27:38 +0000981 MagickBooleanType
982 status;
983
cristy5f959472010-05-27 22:19:46 +0000984 MagickOffsetType
985 progress;
986
987 ssize_t
988 y;
989
cristy351842f2010-03-07 15:27:38 +0000990 assert(image != (Image *) NULL);
991 assert(image->signature == MagickSignature);
992 if (image->debug != MagickFalse)
993 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
994 assert(exception != (ExceptionInfo *) NULL);
995 assert(exception->signature == MagickSignature);
996 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
997 {
998 InheritException(exception,&image->exception);
999 return(MagickFalse);
1000 }
1001 status=MagickTrue;
1002 progress=0;
1003 image_view=AcquireCacheView(image);
1004#if defined(MAGICKCORE_OPENMP_SUPPORT)
1005 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1006#endif
cristybb503372010-05-27 20:51:26 +00001007 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +00001008 {
1009 register IndexPacket
1010 *restrict indexes;
1011
cristybb503372010-05-27 20:51:26 +00001012 register ssize_t
cristy351842f2010-03-07 15:27:38 +00001013 x;
1014
1015 register PixelPacket
1016 *restrict q;
1017
1018 if (status == MagickFalse)
1019 continue;
1020 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1021 if (q == (PixelPacket *) NULL)
1022 {
1023 status=MagickFalse;
1024 continue;
1025 }
1026 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001027 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001028 {
1029 if ((channel & RedChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +00001030 SetRedPixelComponent(q,ApplyFunction(GetRedPixelComponent(q),
1031 function,number_parameters,parameters,exception));
cristy351842f2010-03-07 15:27:38 +00001032 if ((channel & GreenChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +00001033 SetGreenPixelComponent(q,ApplyFunction(GetGreenPixelComponent(q),
1034 function,number_parameters,parameters,exception));
cristy351842f2010-03-07 15:27:38 +00001035 if ((channel & BlueChannel) != 0)
cristyfba5a8b2011-05-03 17:12:12 +00001036 SetBluePixelComponent(q,ApplyFunction(GetBluePixelComponent(q),
1037 function,number_parameters,parameters,exception));
cristy351842f2010-03-07 15:27:38 +00001038 if ((channel & OpacityChannel) != 0)
1039 {
1040 if (image->matte == MagickFalse)
cristyfba5a8b2011-05-03 17:12:12 +00001041 SetOpacityPixelComponent(q,ApplyFunction(
1042 GetOpacityPixelComponent(q),function,number_parameters,parameters,
1043 exception));
cristy351842f2010-03-07 15:27:38 +00001044 else
cristyfba5a8b2011-05-03 17:12:12 +00001045 SetOpacityPixelComponent(q,QuantumRange-ApplyFunction((Quantum)
cristy351842f2010-03-07 15:27:38 +00001046 GetAlphaPixelComponent(q),function,number_parameters,parameters,
cristyfba5a8b2011-05-03 17:12:12 +00001047 exception));
cristy351842f2010-03-07 15:27:38 +00001048 }
1049 if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
cristyfba5a8b2011-05-03 17:12:12 +00001050 SetIndexPixelComponent(indexes+x,ApplyFunction(GetIndexPixelComponent(
1051 indexes+x),function,number_parameters,parameters,exception));
cristy351842f2010-03-07 15:27:38 +00001052 q++;
1053 }
1054 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1055 status=MagickFalse;
1056 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1057 {
1058 MagickBooleanType
1059 proceed;
1060
1061#if defined(MAGICKCORE_OPENMP_SUPPORT)
1062 #pragma omp critical (MagickCore_FunctionImageChannel)
1063#endif
1064 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1065 if (proceed == MagickFalse)
1066 status=MagickFalse;
1067 }
1068 }
1069 image_view=DestroyCacheView(image_view);
1070 return(status);
1071}
1072
1073/*
1074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1075% %
1076% %
1077% %
cristy3ed852e2009-09-05 21:47:34 +00001078+ G e t I m a g e C h a n n e l E x t r e m a %
1079% %
1080% %
1081% %
1082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083%
1084% GetImageChannelExtrema() returns the extrema of one or more image channels.
1085%
1086% The format of the GetImageChannelExtrema method is:
1087%
1088% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001089% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001090% ExceptionInfo *exception)
1091%
1092% A description of each parameter follows:
1093%
1094% o image: the image.
1095%
1096% o channel: the channel.
1097%
1098% o minima: the minimum value in the channel.
1099%
1100% o maxima: the maximum value in the channel.
1101%
1102% o exception: return any errors or warnings in this structure.
1103%
1104*/
1105
1106MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001107 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001108{
cristy9a9230e2011-04-26 14:56:14 +00001109 return(GetImageChannelExtrema(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001110}
1111
1112MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001113 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001114 ExceptionInfo *exception)
1115{
1116 double
1117 max,
1118 min;
1119
1120 MagickBooleanType
1121 status;
1122
1123 assert(image != (Image *) NULL);
1124 assert(image->signature == MagickSignature);
1125 if (image->debug != MagickFalse)
1126 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1127 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001128 *minima=(size_t) ceil(min-0.5);
1129 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001130 return(status);
1131}
1132
1133/*
1134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135% %
1136% %
1137% %
1138% G e t I m a g e C h a n n e l M e a n %
1139% %
1140% %
1141% %
1142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1143%
1144% GetImageChannelMean() returns the mean and standard deviation of one or more
1145% image channels.
1146%
1147% The format of the GetImageChannelMean method is:
1148%
1149% MagickBooleanType GetImageChannelMean(const Image *image,
1150% const ChannelType channel,double *mean,double *standard_deviation,
1151% ExceptionInfo *exception)
1152%
1153% A description of each parameter follows:
1154%
1155% o image: the image.
1156%
1157% o channel: the channel.
1158%
1159% o mean: the average value in the channel.
1160%
1161% o standard_deviation: the standard deviation of the channel.
1162%
1163% o exception: return any errors or warnings in this structure.
1164%
1165*/
1166
1167MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1168 double *standard_deviation,ExceptionInfo *exception)
1169{
1170 MagickBooleanType
1171 status;
1172
cristy9a9230e2011-04-26 14:56:14 +00001173 status=GetImageChannelMean(image,CompositeChannels,mean,standard_deviation,
cristy3ed852e2009-09-05 21:47:34 +00001174 exception);
1175 return(status);
1176}
1177
1178MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1179 const ChannelType channel,double *mean,double *standard_deviation,
1180 ExceptionInfo *exception)
1181{
cristyfd9dcd42010-08-08 18:07:02 +00001182 ChannelStatistics
1183 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001184
cristyfd9dcd42010-08-08 18:07:02 +00001185 size_t
1186 channels;
cristy3ed852e2009-09-05 21:47:34 +00001187
1188 assert(image != (Image *) NULL);
1189 assert(image->signature == MagickSignature);
1190 if (image->debug != MagickFalse)
1191 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001192 channel_statistics=GetImageChannelStatistics(image,exception);
1193 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001194 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001195 channels=0;
cristy9a9230e2011-04-26 14:56:14 +00001196 channel_statistics[CompositeChannels].mean=0.0;
1197 channel_statistics[CompositeChannels].standard_deviation=0.0;
cristyfd9dcd42010-08-08 18:07:02 +00001198 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
cristy9a9230e2011-04-26 14:56:14 +00001200 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001201 channel_statistics[RedChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001202 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001203 channel_statistics[RedChannel].variance-
1204 channel_statistics[RedChannel].mean*
1205 channel_statistics[RedChannel].mean;
1206 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001207 }
cristyfd9dcd42010-08-08 18:07:02 +00001208 if ((channel & GreenChannel) != 0)
1209 {
cristy9a9230e2011-04-26 14:56:14 +00001210 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001211 channel_statistics[GreenChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001212 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001213 channel_statistics[GreenChannel].variance-
1214 channel_statistics[GreenChannel].mean*
1215 channel_statistics[GreenChannel].mean;
1216 channels++;
1217 }
1218 if ((channel & BlueChannel) != 0)
1219 {
cristy9a9230e2011-04-26 14:56:14 +00001220 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001221 channel_statistics[BlueChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001222 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001223 channel_statistics[BlueChannel].variance-
1224 channel_statistics[BlueChannel].mean*
1225 channel_statistics[BlueChannel].mean;
1226 channels++;
1227 }
1228 if (((channel & OpacityChannel) != 0) &&
1229 (image->matte != MagickFalse))
1230 {
cristy9a9230e2011-04-26 14:56:14 +00001231 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001232 channel_statistics[OpacityChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001233 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001234 channel_statistics[OpacityChannel].variance-
1235 channel_statistics[OpacityChannel].mean*
1236 channel_statistics[OpacityChannel].mean;
1237 channels++;
1238 }
1239 if (((channel & IndexChannel) != 0) &&
1240 (image->colorspace == CMYKColorspace))
1241 {
cristy9a9230e2011-04-26 14:56:14 +00001242 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001243 channel_statistics[BlackChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001244 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001245 channel_statistics[BlackChannel].variance-
1246 channel_statistics[BlackChannel].mean*
1247 channel_statistics[BlackChannel].mean;
1248 channels++;
1249 }
cristy9a9230e2011-04-26 14:56:14 +00001250 channel_statistics[CompositeChannels].mean/=channels;
1251 channel_statistics[CompositeChannels].standard_deviation=
1252 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1253 *mean=channel_statistics[CompositeChannels].mean;
1254 *standard_deviation=channel_statistics[CompositeChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001255 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1256 channel_statistics);
1257 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001258}
1259
1260/*
1261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262% %
1263% %
1264% %
1265% G e t I m a g e C h a n n e l K u r t o s i s %
1266% %
1267% %
1268% %
1269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270%
1271% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1272% image channels.
1273%
1274% The format of the GetImageChannelKurtosis method is:
1275%
1276% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1277% const ChannelType channel,double *kurtosis,double *skewness,
1278% ExceptionInfo *exception)
1279%
1280% A description of each parameter follows:
1281%
1282% o image: the image.
1283%
1284% o channel: the channel.
1285%
1286% o kurtosis: the kurtosis of the channel.
1287%
1288% o skewness: the skewness of the channel.
1289%
1290% o exception: return any errors or warnings in this structure.
1291%
1292*/
1293
1294MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1295 double *kurtosis,double *skewness,ExceptionInfo *exception)
1296{
1297 MagickBooleanType
1298 status;
1299
cristy9a9230e2011-04-26 14:56:14 +00001300 status=GetImageChannelKurtosis(image,CompositeChannels,kurtosis,skewness,
cristy3ed852e2009-09-05 21:47:34 +00001301 exception);
1302 return(status);
1303}
1304
1305MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1306 const ChannelType channel,double *kurtosis,double *skewness,
1307 ExceptionInfo *exception)
1308{
1309 double
1310 area,
1311 mean,
1312 standard_deviation,
1313 sum_squares,
1314 sum_cubes,
1315 sum_fourth_power;
1316
cristybb503372010-05-27 20:51:26 +00001317 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001318 y;
1319
1320 assert(image != (Image *) NULL);
1321 assert(image->signature == MagickSignature);
1322 if (image->debug != MagickFalse)
1323 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1324 *kurtosis=0.0;
1325 *skewness=0.0;
1326 area=0.0;
1327 mean=0.0;
1328 standard_deviation=0.0;
1329 sum_squares=0.0;
1330 sum_cubes=0.0;
1331 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001332 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001333 {
1334 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001335 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001336
1337 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001338 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001339
cristybb503372010-05-27 20:51:26 +00001340 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001341 x;
1342
1343 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1344 if (p == (const PixelPacket *) NULL)
1345 break;
1346 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001347 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001348 {
1349 if ((channel & RedChannel) != 0)
1350 {
cristyce70c172010-01-07 17:15:30 +00001351 mean+=GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001352 sum_squares+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p);
cristyfba5a8b2011-05-03 17:12:12 +00001353 sum_cubes+=(double) GetRedPixelComponent(p)*GetRedPixelComponent(p)*
1354 GetRedPixelComponent(p);
1355 sum_fourth_power+=(double) GetRedPixelComponent(p)*
1356 GetRedPixelComponent(p)*GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001357 GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001358 area++;
1359 }
1360 if ((channel & GreenChannel) != 0)
1361 {
cristyce70c172010-01-07 17:15:30 +00001362 mean+=GetGreenPixelComponent(p);
cristyfba5a8b2011-05-03 17:12:12 +00001363 sum_squares+=(double) GetGreenPixelComponent(p)*
1364 GetGreenPixelComponent(p);
1365 sum_cubes+=(double) GetGreenPixelComponent(p)*
1366 GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
1367 sum_fourth_power+=(double) GetGreenPixelComponent(p)*
1368 GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001369 GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001370 area++;
1371 }
1372 if ((channel & BlueChannel) != 0)
1373 {
cristyce70c172010-01-07 17:15:30 +00001374 mean+=GetBluePixelComponent(p);
cristyfba5a8b2011-05-03 17:12:12 +00001375 sum_squares+=(double) GetBluePixelComponent(p)*
1376 GetBluePixelComponent(p);
1377 sum_cubes+=(double) GetBluePixelComponent(p)*GetBluePixelComponent(p)*
1378 GetBluePixelComponent(p);
1379 sum_fourth_power+=(double) GetBluePixelComponent(p)*
1380 GetBluePixelComponent(p)*GetBluePixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001381 GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001382 area++;
1383 }
1384 if ((channel & OpacityChannel) != 0)
1385 {
cristyce70c172010-01-07 17:15:30 +00001386 mean+=GetOpacityPixelComponent(p);
cristyfba5a8b2011-05-03 17:12:12 +00001387 sum_squares+=(double) GetOpacityPixelComponent(p)*
1388 GetOpacityPixelComponent(p);
1389 sum_cubes+=(double) GetOpacityPixelComponent(p)*
1390 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
1391 sum_fourth_power+=(double) GetOpacityPixelComponent(p)*
1392 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001393 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001394 area++;
1395 }
1396 if (((channel & IndexChannel) != 0) &&
1397 (image->colorspace == CMYKColorspace))
1398 {
cristyfba5a8b2011-05-03 17:12:12 +00001399 mean+=GetIndexPixelComponent(indexes+x);
1400 sum_squares+=(double) GetIndexPixelComponent(indexes+x)*
1401 GetIndexPixelComponent(indexes+x);
1402 sum_cubes+=(double) GetIndexPixelComponent(indexes+x)*
1403 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x);
1404 sum_fourth_power+=(double) GetIndexPixelComponent(indexes+x)*
1405 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x)*
1406 GetIndexPixelComponent(indexes+x);
cristy3ed852e2009-09-05 21:47:34 +00001407 area++;
1408 }
1409 p++;
1410 }
1411 }
cristybb503372010-05-27 20:51:26 +00001412 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001413 return(MagickFalse);
1414 if (area != 0.0)
1415 {
1416 mean/=area;
1417 sum_squares/=area;
1418 sum_cubes/=area;
1419 sum_fourth_power/=area;
1420 }
1421 standard_deviation=sqrt(sum_squares-(mean*mean));
1422 if (standard_deviation != 0.0)
1423 {
1424 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1425 3.0*mean*mean*mean*mean;
1426 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1427 standard_deviation;
1428 *kurtosis-=3.0;
1429 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1430 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1431 }
cristybb503372010-05-27 20:51:26 +00001432 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001433}
cristy46f08202010-01-10 04:04:21 +00001434
cristy3ed852e2009-09-05 21:47:34 +00001435/*
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437% %
1438% %
1439% %
1440% G e t I m a g e C h a n n e l R a n g e %
1441% %
1442% %
1443% %
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%
1446% GetImageChannelRange() returns the range of one or more image channels.
1447%
1448% The format of the GetImageChannelRange method is:
1449%
1450% MagickBooleanType GetImageChannelRange(const Image *image,
1451% const ChannelType channel,double *minima,double *maxima,
1452% ExceptionInfo *exception)
1453%
1454% A description of each parameter follows:
1455%
1456% o image: the image.
1457%
1458% o channel: the channel.
1459%
1460% o minima: the minimum value in the channel.
1461%
1462% o maxima: the maximum value in the channel.
1463%
1464% o exception: return any errors or warnings in this structure.
1465%
1466*/
1467
1468MagickExport MagickBooleanType GetImageRange(const Image *image,
1469 double *minima,double *maxima,ExceptionInfo *exception)
1470{
cristy9a9230e2011-04-26 14:56:14 +00001471 return(GetImageChannelRange(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001472}
1473
1474MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1475 const ChannelType channel,double *minima,double *maxima,
1476 ExceptionInfo *exception)
1477{
cristy3ed852e2009-09-05 21:47:34 +00001478 MagickPixelPacket
1479 pixel;
1480
cristy9d314ff2011-03-09 01:30:28 +00001481 ssize_t
1482 y;
1483
cristy3ed852e2009-09-05 21:47:34 +00001484 assert(image != (Image *) NULL);
1485 assert(image->signature == MagickSignature);
1486 if (image->debug != MagickFalse)
1487 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1488 *maxima=(-1.0E-37);
1489 *minima=1.0E+37;
1490 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001491 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001492 {
1493 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001494 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001495
1496 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001497 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001498
cristybb503372010-05-27 20:51:26 +00001499 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001500 x;
1501
1502 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1503 if (p == (const PixelPacket *) NULL)
1504 break;
1505 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001506 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001507 {
1508 SetMagickPixelPacket(image,p,indexes+x,&pixel);
1509 if ((channel & RedChannel) != 0)
1510 {
1511 if (pixel.red < *minima)
1512 *minima=(double) pixel.red;
1513 if (pixel.red > *maxima)
1514 *maxima=(double) pixel.red;
1515 }
1516 if ((channel & GreenChannel) != 0)
1517 {
1518 if (pixel.green < *minima)
1519 *minima=(double) pixel.green;
1520 if (pixel.green > *maxima)
1521 *maxima=(double) pixel.green;
1522 }
1523 if ((channel & BlueChannel) != 0)
1524 {
1525 if (pixel.blue < *minima)
1526 *minima=(double) pixel.blue;
1527 if (pixel.blue > *maxima)
1528 *maxima=(double) pixel.blue;
1529 }
1530 if ((channel & OpacityChannel) != 0)
1531 {
1532 if (pixel.opacity < *minima)
1533 *minima=(double) pixel.opacity;
1534 if (pixel.opacity > *maxima)
1535 *maxima=(double) pixel.opacity;
1536 }
1537 if (((channel & IndexChannel) != 0) &&
1538 (image->colorspace == CMYKColorspace))
1539 {
cristyfba5a8b2011-05-03 17:12:12 +00001540 if ((double) GetIndexPixelComponent(indexes+x) < *minima)
1541 *minima=(double) GetIndexPixelComponent(indexes+x);
1542 if ((double) GetIndexPixelComponent(indexes+x) > *maxima)
1543 *maxima=(double) GetIndexPixelComponent(indexes+x);
cristy3ed852e2009-09-05 21:47:34 +00001544 }
1545 p++;
1546 }
1547 }
cristybb503372010-05-27 20:51:26 +00001548 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001549}
1550
1551/*
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553% %
1554% %
1555% %
1556% 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 %
1557% %
1558% %
1559% %
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561%
1562% GetImageChannelStatistics() returns statistics for each channel in the
1563% image. The statistics include the channel depth, its minima, maxima, mean,
1564% standard deviation, kurtosis and skewness. You can access the red channel
1565% mean, for example, like this:
1566%
cristy0c1c3fd2011-01-18 15:53:10 +00001567% channel_statistics=GetImageChannelStatistics(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001568% red_mean=channel_statistics[RedChannel].mean;
1569%
1570% Use MagickRelinquishMemory() to free the statistics buffer.
1571%
1572% The format of the GetImageChannelStatistics method is:
1573%
1574% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1575% ExceptionInfo *exception)
1576%
1577% A description of each parameter follows:
1578%
1579% o image: the image.
1580%
1581% o exception: return any errors or warnings in this structure.
1582%
1583*/
cristy3ed852e2009-09-05 21:47:34 +00001584MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1585 ExceptionInfo *exception)
1586{
1587 ChannelStatistics
1588 *channel_statistics;
1589
1590 double
cristyfd9dcd42010-08-08 18:07:02 +00001591 area;
cristy3ed852e2009-09-05 21:47:34 +00001592
cristy3ed852e2009-09-05 21:47:34 +00001593 MagickStatusType
1594 status;
1595
1596 QuantumAny
1597 range;
1598
cristybb503372010-05-27 20:51:26 +00001599 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001600 i;
1601
1602 size_t
cristy9d314ff2011-03-09 01:30:28 +00001603 channels,
1604 depth,
cristy3ed852e2009-09-05 21:47:34 +00001605 length;
1606
cristy9d314ff2011-03-09 01:30:28 +00001607 ssize_t
1608 y;
cristy3ed852e2009-09-05 21:47:34 +00001609
1610 assert(image != (Image *) NULL);
1611 assert(image->signature == MagickSignature);
1612 if (image->debug != MagickFalse)
1613 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy9a9230e2011-04-26 14:56:14 +00001614 length=CompositeChannels+1UL;
cristy3ed852e2009-09-05 21:47:34 +00001615 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1616 sizeof(*channel_statistics));
1617 if (channel_statistics == (ChannelStatistics *) NULL)
1618 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1619 (void) ResetMagickMemory(channel_statistics,0,length*
1620 sizeof(*channel_statistics));
cristy32daba42011-05-01 02:34:39 +00001621 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001622 {
1623 channel_statistics[i].depth=1;
1624 channel_statistics[i].maxima=(-1.0E-37);
1625 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001626 }
cristybb503372010-05-27 20:51:26 +00001627 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001628 {
1629 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001630 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001631
1632 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001633 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001634
cristybb503372010-05-27 20:51:26 +00001635 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001636 x;
1637
1638 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1639 if (p == (const PixelPacket *) NULL)
1640 break;
1641 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001642 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001643 {
1644 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1645 {
1646 depth=channel_statistics[RedChannel].depth;
1647 range=GetQuantumRange(depth);
cristy32daba42011-05-01 02:34:39 +00001648 status=GetRedPixelComponent(p) != ScaleAnyToQuantum(
1649 ScaleQuantumToAny(GetRedPixelComponent(p),range),range) ?
1650 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001651 if (status != MagickFalse)
1652 {
1653 channel_statistics[RedChannel].depth++;
1654 continue;
1655 }
1656 }
1657 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1658 {
1659 depth=channel_statistics[GreenChannel].depth;
1660 range=GetQuantumRange(depth);
cristy32daba42011-05-01 02:34:39 +00001661 status=GetGreenPixelComponent(p) != ScaleAnyToQuantum(
1662 ScaleQuantumToAny(GetGreenPixelComponent(p),range),range) ?
1663 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001664 if (status != MagickFalse)
1665 {
1666 channel_statistics[GreenChannel].depth++;
1667 continue;
1668 }
1669 }
1670 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1671 {
1672 depth=channel_statistics[BlueChannel].depth;
1673 range=GetQuantumRange(depth);
cristy32daba42011-05-01 02:34:39 +00001674 status=GetBluePixelComponent(p) != ScaleAnyToQuantum(
1675 ScaleQuantumToAny(GetBluePixelComponent(p),range),range) ?
1676 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001677 if (status != MagickFalse)
1678 {
1679 channel_statistics[BlueChannel].depth++;
1680 continue;
1681 }
1682 }
1683 if (image->matte != MagickFalse)
1684 {
1685 if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1686 {
1687 depth=channel_statistics[OpacityChannel].depth;
1688 range=GetQuantumRange(depth);
cristy32daba42011-05-01 02:34:39 +00001689 status=GetOpacityPixelComponent(p) != ScaleAnyToQuantum(
1690 ScaleQuantumToAny(GetOpacityPixelComponent(p),range),range) ?
1691 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001692 if (status != MagickFalse)
1693 {
1694 channel_statistics[OpacityChannel].depth++;
1695 continue;
1696 }
1697 }
1698 }
1699 if (image->colorspace == CMYKColorspace)
1700 {
1701 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1702 {
1703 depth=channel_statistics[BlackChannel].depth;
1704 range=GetQuantumRange(depth);
cristyfba5a8b2011-05-03 17:12:12 +00001705 status=GetIndexPixelComponent(indexes+x) !=
1706 ScaleAnyToQuantum(ScaleQuantumToAny(GetIndexPixelComponent(
1707 indexes+x),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001708 if (status != MagickFalse)
1709 {
1710 channel_statistics[BlackChannel].depth++;
1711 continue;
1712 }
1713 }
1714 }
cristyd05ecd12011-04-22 20:44:42 +00001715 if ((double) GetRedPixelComponent(p) < channel_statistics[RedChannel].minima)
cristyce70c172010-01-07 17:15:30 +00001716 channel_statistics[RedChannel].minima=(double) GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001717 if ((double) GetRedPixelComponent(p) > channel_statistics[RedChannel].maxima)
cristyce70c172010-01-07 17:15:30 +00001718 channel_statistics[RedChannel].maxima=(double) GetRedPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001719 channel_statistics[RedChannel].sum+=GetRedPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001720 channel_statistics[RedChannel].sum_squared+=(double) GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001721 GetRedPixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001722 channel_statistics[RedChannel].sum_cubed+=(double)
1723 GetRedPixelComponent(p)*GetRedPixelComponent(p)*
cristy46f08202010-01-10 04:04:21 +00001724 GetRedPixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001725 channel_statistics[RedChannel].sum_fourth_power+=(double)
1726 GetRedPixelComponent(p)*GetRedPixelComponent(p)*
cristyd05ecd12011-04-22 20:44:42 +00001727 GetRedPixelComponent(p)*GetRedPixelComponent(p);
1728 if ((double) GetGreenPixelComponent(p) < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001729 channel_statistics[GreenChannel].minima=(double)
1730 GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001731 if ((double) GetGreenPixelComponent(p) > channel_statistics[GreenChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001732 channel_statistics[GreenChannel].maxima=(double)
1733 GetGreenPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001734 channel_statistics[GreenChannel].sum+=GetGreenPixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001735 channel_statistics[GreenChannel].sum_squared+=(double)
1736 GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
1737 channel_statistics[GreenChannel].sum_cubed+=(double)
1738 GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001739 GetGreenPixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001740 channel_statistics[GreenChannel].sum_fourth_power+=(double)
1741 GetGreenPixelComponent(p)*GetGreenPixelComponent(p)*
1742 GetGreenPixelComponent(p)*GetGreenPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001743 if ((double) GetBluePixelComponent(p) < channel_statistics[BlueChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001744 channel_statistics[BlueChannel].minima=(double)
1745 GetBluePixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001746 if ((double) GetBluePixelComponent(p) > channel_statistics[BlueChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001747 channel_statistics[BlueChannel].maxima=(double)
1748 GetBluePixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001749 channel_statistics[BlueChannel].sum+=GetBluePixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001750 channel_statistics[BlueChannel].sum_squared+=(double)
1751 GetBluePixelComponent(p)*GetBluePixelComponent(p);
1752 channel_statistics[BlueChannel].sum_cubed+=(double)
1753 GetBluePixelComponent(p)*GetBluePixelComponent(p)*
cristyce70c172010-01-07 17:15:30 +00001754 GetBluePixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001755 channel_statistics[BlueChannel].sum_fourth_power+=(double)
1756 GetBluePixelComponent(p)*GetBluePixelComponent(p)*
1757 GetBluePixelComponent(p)*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001758 if (image->matte != MagickFalse)
1759 {
cristyd05ecd12011-04-22 20:44:42 +00001760 if ((double) GetOpacityPixelComponent(p) < channel_statistics[OpacityChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001761 channel_statistics[OpacityChannel].minima=(double)
1762 GetOpacityPixelComponent(p);
cristyd05ecd12011-04-22 20:44:42 +00001763 if ((double) GetOpacityPixelComponent(p) > channel_statistics[OpacityChannel].maxima)
cristy46f08202010-01-10 04:04:21 +00001764 channel_statistics[OpacityChannel].maxima=(double)
1765 GetOpacityPixelComponent(p);
cristyfd9dcd42010-08-08 18:07:02 +00001766 channel_statistics[OpacityChannel].sum+=GetOpacityPixelComponent(p);
1767 channel_statistics[OpacityChannel].sum_squared+=(double)
cristyd05ecd12011-04-22 20:44:42 +00001768 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
cristy32daba42011-05-01 02:34:39 +00001769 channel_statistics[OpacityChannel].sum_cubed+=(double)
1770 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*
1771 GetOpacityPixelComponent(p);
cristya8178ed2010-08-10 17:31:59 +00001772 channel_statistics[OpacityChannel].sum_fourth_power+=(double)
cristy32daba42011-05-01 02:34:39 +00001773 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p)*
1774 GetOpacityPixelComponent(p)*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00001775 }
1776 if (image->colorspace == CMYKColorspace)
1777 {
cristyfba5a8b2011-05-03 17:12:12 +00001778 if ((double) GetIndexPixelComponent(indexes+x) < channel_statistics[BlackChannel].minima)
1779 channel_statistics[BlackChannel].minima=(double)
1780 GetIndexPixelComponent(indexes+x);
1781 if ((double) GetIndexPixelComponent(indexes+x) > channel_statistics[BlackChannel].maxima)
1782 channel_statistics[BlackChannel].maxima=(double)
1783 GetIndexPixelComponent(indexes+x);
1784 channel_statistics[BlackChannel].sum+=
1785 GetIndexPixelComponent(indexes+x);
cristyfd9dcd42010-08-08 18:07:02 +00001786 channel_statistics[BlackChannel].sum_squared+=(double)
cristyfba5a8b2011-05-03 17:12:12 +00001787 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x);
1788 channel_statistics[BlackChannel].sum_cubed+=(double)
1789 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x)*
1790 GetIndexPixelComponent(indexes+x);
cristya8178ed2010-08-10 17:31:59 +00001791 channel_statistics[BlackChannel].sum_fourth_power+=(double)
cristyfba5a8b2011-05-03 17:12:12 +00001792 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x)*
1793 GetIndexPixelComponent(indexes+x)*GetIndexPixelComponent(indexes+x);
cristy3ed852e2009-09-05 21:47:34 +00001794 }
1795 x++;
1796 p++;
1797 }
1798 }
1799 area=(double) image->columns*image->rows;
cristy32daba42011-05-01 02:34:39 +00001800 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001801 {
cristyfd9dcd42010-08-08 18:07:02 +00001802 channel_statistics[i].sum/=area;
1803 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001804 channel_statistics[i].sum_cubed/=area;
1805 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001806 channel_statistics[i].mean=channel_statistics[i].sum;
1807 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1808 channel_statistics[i].standard_deviation=sqrt(
1809 channel_statistics[i].variance-(channel_statistics[i].mean*
1810 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001811 }
cristy32daba42011-05-01 02:34:39 +00001812 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001813 {
cristy9a9230e2011-04-26 14:56:14 +00001814 channel_statistics[CompositeChannels].depth=(size_t) MagickMax((double)
1815 channel_statistics[CompositeChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001816 channel_statistics[i].depth);
cristy9a9230e2011-04-26 14:56:14 +00001817 channel_statistics[CompositeChannels].minima=MagickMin(
cristy32daba42011-05-01 02:34:39 +00001818 channel_statistics[CompositeChannels].minima,
1819 channel_statistics[i].minima);
cristy9a9230e2011-04-26 14:56:14 +00001820 channel_statistics[CompositeChannels].maxima=MagickMax(
cristy32daba42011-05-01 02:34:39 +00001821 channel_statistics[CompositeChannels].maxima,
1822 channel_statistics[i].maxima);
cristy9a9230e2011-04-26 14:56:14 +00001823 channel_statistics[CompositeChannels].sum+=channel_statistics[i].sum;
1824 channel_statistics[CompositeChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001825 channel_statistics[i].sum_squared;
cristy32daba42011-05-01 02:34:39 +00001826 channel_statistics[CompositeChannels].sum_cubed+=
1827 channel_statistics[i].sum_cubed;
cristy9a9230e2011-04-26 14:56:14 +00001828 channel_statistics[CompositeChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001829 channel_statistics[i].sum_fourth_power;
cristy9a9230e2011-04-26 14:56:14 +00001830 channel_statistics[CompositeChannels].mean+=channel_statistics[i].mean;
cristy32daba42011-05-01 02:34:39 +00001831 channel_statistics[CompositeChannels].variance+=
1832 channel_statistics[i].variance-channel_statistics[i].mean*
1833 channel_statistics[i].mean;
cristy9a9230e2011-04-26 14:56:14 +00001834 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001835 channel_statistics[i].variance-channel_statistics[i].mean*
1836 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001837 }
cristycf584452010-08-08 02:26:04 +00001838 channels=3;
1839 if (image->matte != MagickFalse)
1840 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001841 if (image->colorspace == CMYKColorspace)
1842 channels++;
cristy9a9230e2011-04-26 14:56:14 +00001843 channel_statistics[CompositeChannels].sum/=channels;
1844 channel_statistics[CompositeChannels].sum_squared/=channels;
1845 channel_statistics[CompositeChannels].sum_cubed/=channels;
1846 channel_statistics[CompositeChannels].sum_fourth_power/=channels;
1847 channel_statistics[CompositeChannels].mean/=channels;
1848 channel_statistics[CompositeChannels].variance/=channels;
1849 channel_statistics[CompositeChannels].standard_deviation=
1850 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1851 channel_statistics[CompositeChannels].kurtosis/=channels;
1852 channel_statistics[CompositeChannels].skewness/=channels;
cristy32daba42011-05-01 02:34:39 +00001853 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001854 {
cristy3ed852e2009-09-05 21:47:34 +00001855 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001856 continue;
1857 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1858 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1859 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1860 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1861 channel_statistics[i].standard_deviation*
1862 channel_statistics[i].standard_deviation);
1863 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1864 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1865 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1866 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1867 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1868 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1869 channel_statistics[i].standard_deviation*
1870 channel_statistics[i].standard_deviation*
1871 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001872 }
1873 return(channel_statistics);
1874}