blob: ed63d5da4c3d56f2a93be261fe442dad8bed174a [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*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/animate.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/cache-private.h"
50#include "MagickCore/cache-view.h"
51#include "MagickCore/client.h"
52#include "MagickCore/color.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/composite.h"
57#include "MagickCore/composite-private.h"
58#include "MagickCore/compress.h"
59#include "MagickCore/constitute.h"
60#include "MagickCore/display.h"
61#include "MagickCore/draw.h"
62#include "MagickCore/enhance.h"
63#include "MagickCore/exception.h"
64#include "MagickCore/exception-private.h"
65#include "MagickCore/gem.h"
66#include "MagickCore/geometry.h"
67#include "MagickCore/list.h"
68#include "MagickCore/image-private.h"
69#include "MagickCore/magic.h"
70#include "MagickCore/magick.h"
71#include "MagickCore/memory_.h"
72#include "MagickCore/module.h"
73#include "MagickCore/monitor.h"
74#include "MagickCore/monitor-private.h"
75#include "MagickCore/option.h"
76#include "MagickCore/paint.h"
77#include "MagickCore/pixel-accessor.h"
78#include "MagickCore/profile.h"
79#include "MagickCore/quantize.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/random_.h"
82#include "MagickCore/random-private.h"
83#include "MagickCore/segment.h"
84#include "MagickCore/semaphore.h"
85#include "MagickCore/signature-private.h"
86#include "MagickCore/statistic.h"
87#include "MagickCore/string_.h"
88#include "MagickCore/thread-private.h"
89#include "MagickCore/timer.h"
90#include "MagickCore/utility.h"
91#include "MagickCore/version.h"
cristy3ed852e2009-09-05 21:47:34 +000092
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
cristy4c08aed2011-07-01 19:47:50 +0000135static PixelInfo **DestroyPixelThreadSet(PixelInfo **pixels)
cristy316d5172009-09-17 19:31:25 +0000136{
cristybb503372010-05-27 20:51:26 +0000137 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000138 i;
139
cristy4c08aed2011-07-01 19:47:50 +0000140 assert(pixels != (PixelInfo **) NULL);
cristybb503372010-05-27 20:51:26 +0000141 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy4c08aed2011-07-01 19:47:50 +0000142 if (pixels[i] != (PixelInfo *) NULL)
143 pixels[i]=(PixelInfo *) RelinquishMagickMemory(pixels[i]);
144 pixels=(PixelInfo **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000145 return(pixels);
146}
147
cristy4c08aed2011-07-01 19:47:50 +0000148static PixelInfo **AcquirePixelThreadSet(const Image *image,
cristy08a3d702010-11-28 01:57:36 +0000149 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
cristy4c08aed2011-07-01 19:47:50 +0000155 PixelInfo
cristy316d5172009-09-17 19:31:25 +0000156 **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();
cristy4c08aed2011-07-01 19:47:50 +0000163 pixels=(PixelInfo **) AcquireQuantumMemory(number_threads,
cristy316d5172009-09-17 19:31:25 +0000164 sizeof(*pixels));
cristy4c08aed2011-07-01 19:47:50 +0000165 if (pixels == (PixelInfo **) NULL)
166 return((PixelInfo **) NULL);
cristy316d5172009-09-17 19:31:25 +0000167 (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;
cristy4c08aed2011-07-01 19:47:50 +0000173 pixels[i]=(PixelInfo *) AcquireQuantumMemory(length,
cristy316d5172009-09-17 19:31:25 +0000174 sizeof(**pixels));
cristy4c08aed2011-07-01 19:47:50 +0000175 if (pixels[i] == (PixelInfo *) NULL)
cristy316d5172009-09-17 19:31:25 +0000176 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000177 for (j=0; j < (ssize_t) length; j++)
cristy4c08aed2011-07-01 19:47:50 +0000178 GetPixelInfo(image,&pixels[i][j]);
cristy316d5172009-09-17 19:31:25 +0000179 }
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{
cristy4c08aed2011-07-01 19:47:50 +0000196 const PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000197 *color_1,
198 *color_2;
199
200 int
201 intensity;
202
cristy4c08aed2011-07-01 19:47:50 +0000203 color_1=(const PixelInfo *) x;
204 color_2=(const PixelInfo *) y;
205 intensity=(int) GetPixelInfoIntensity(color_2)-(int)
206 GetPixelInfoIntensity(color_1);
cristy08a3d702010-11-28 01:57:36 +0000207 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
cristy4c08aed2011-07-01 19:47:50 +0000436 PixelInfo
cristyd18ae7c2010-03-07 17:39:52 +0000437 **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);
cristy4c08aed2011-07-01 19:47:50 +0000480 if (evaluate_pixels == (PixelInfo **) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000481 {
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;
cristy4c08aed2011-07-01 19:47:50 +0000492 GetPixelInfo(images,&zero);
cristyd18ae7c2010-03-07 17:39:52 +0000493 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
cristy4c08aed2011-07-01 19:47:50 +0000510 register PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000511 *evaluate_pixel;
512
cristy4c08aed2011-07-01 19:47:50 +0000513 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000514 *restrict q;
515
516 register ssize_t
517 x;
518
519 if (status == MagickFalse)
520 continue;
521 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
522 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000523 if (q == (const Quantum *) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000524 {
cristy08a3d702010-11-28 01:57:36 +0000525 status=MagickFalse;
526 continue;
cristyd18ae7c2010-03-07 17:39:52 +0000527 }
cristy08a3d702010-11-28 01:57:36 +0000528 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000529 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000530 {
cristy08a3d702010-11-28 01:57:36 +0000531 register ssize_t
532 i;
533
534 for (i=0; i < (ssize_t) number_images; i++)
535 evaluate_pixel[i]=zero;
536 next=images;
537 for (i=0; i < (ssize_t) number_images; i++)
538 {
cristy4c08aed2011-07-01 19:47:50 +0000539 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000540 *p;
541
542 image_view=AcquireCacheView(next);
543 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000544 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000545 {
546 image_view=DestroyCacheView(image_view);
547 break;
548 }
cristy08a3d702010-11-28 01:57:36 +0000549 evaluate_pixel[i].red=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000550 GetPixelRed(next,p),op,evaluate_pixel[i].red);
cristy08a3d702010-11-28 01:57:36 +0000551 evaluate_pixel[i].green=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000552 GetPixelGreen(next,p),op,evaluate_pixel[i].green);
cristy08a3d702010-11-28 01:57:36 +0000553 evaluate_pixel[i].blue=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000554 GetPixelBlue(next,p),op,evaluate_pixel[i].blue);
cristy08a3d702010-11-28 01:57:36 +0000555 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000556 evaluate_pixel[i].black=ApplyEvaluateOperator(random_info[id],
557 GetPixelBlack(next,p),op,evaluate_pixel[i].black);
558 evaluate_pixel[i].alpha=ApplyEvaluateOperator(random_info[id],
559 GetPixelAlpha(next,p),op,evaluate_pixel[i].alpha);
cristy08a3d702010-11-28 01:57:36 +0000560 image_view=DestroyCacheView(image_view);
561 next=GetNextImageInList(next);
562 }
563 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
564 IntensityCompare);
cristy4c08aed2011-07-01 19:47:50 +0000565 SetPixelRed(evaluate_image,
566 ClampToQuantum(evaluate_pixel[i/2].red),q);
567 SetPixelGreen(evaluate_image,
568 ClampToQuantum(evaluate_pixel[i/2].green),q);
569 SetPixelBlue(evaluate_image,
570 ClampToQuantum(evaluate_pixel[i/2].blue),q);
cristy08a3d702010-11-28 01:57:36 +0000571 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000572 SetPixelBlack(evaluate_image,
573 ClampToQuantum(evaluate_pixel[i/2].black),q);
574 if (evaluate_image->matte == MagickFalse)
575 SetPixelAlpha(evaluate_image,
576 ClampToQuantum(evaluate_pixel[i/2].alpha),q);
577 else
578 SetPixelAlpha(evaluate_image,
579 ClampToQuantum(evaluate_pixel[i/2].alpha),q);
580 q+=GetPixelChannels(evaluate_image);
cristy125a5a32010-05-07 13:30:52 +0000581 }
cristy08a3d702010-11-28 01:57:36 +0000582 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
583 status=MagickFalse;
584 if (images->progress_monitor != (MagickProgressMonitor) NULL)
585 {
586 MagickBooleanType
587 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000588
589#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000590 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000591#endif
cristy08a3d702010-11-28 01:57:36 +0000592 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
593 evaluate_image->rows);
594 if (proceed == MagickFalse)
595 status=MagickFalse;
596 }
597 }
598 else
599#if defined(MAGICKCORE_OPENMP_SUPPORT)
600 #pragma omp parallel for schedule(dynamic) shared(progress,status)
601#endif
602 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
603 {
604 CacheView
605 *image_view;
606
607 const Image
608 *next;
609
610 const int
611 id = GetOpenMPThreadId();
612
cristy08a3d702010-11-28 01:57:36 +0000613 register ssize_t
614 i,
615 x;
616
cristy4c08aed2011-07-01 19:47:50 +0000617 register PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000618 *evaluate_pixel;
619
cristy4c08aed2011-07-01 19:47:50 +0000620 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000621 *restrict q;
622
623 if (status == MagickFalse)
624 continue;
625 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
626 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000627 if (q == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000628 {
cristyd18ae7c2010-03-07 17:39:52 +0000629 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000630 continue;
631 }
cristy08a3d702010-11-28 01:57:36 +0000632 evaluate_pixel=evaluate_pixels[id];
633 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
634 evaluate_pixel[x]=zero;
635 next=images;
636 for (i=0; i < (ssize_t) number_images; i++)
637 {
cristy4c08aed2011-07-01 19:47:50 +0000638 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000639 *p;
640
641 image_view=AcquireCacheView(next);
642 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000643 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000644 {
645 image_view=DestroyCacheView(image_view);
646 break;
647 }
cristy08a3d702010-11-28 01:57:36 +0000648 for (x=0; x < (ssize_t) next->columns; x++)
649 {
650 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000651 GetPixelRed(next,p),i == 0 ? AddEvaluateOperator : op,
652 evaluate_pixel[x].red);
cristy08a3d702010-11-28 01:57:36 +0000653 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000654 GetPixelGreen(next,p),i == 0 ? AddEvaluateOperator : op,
655 evaluate_pixel[x].green);
cristy08a3d702010-11-28 01:57:36 +0000656 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000657 GetPixelBlue(next,p),i == 0 ? AddEvaluateOperator : op,
658 evaluate_pixel[x].blue);
cristy08a3d702010-11-28 01:57:36 +0000659 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000660 evaluate_pixel[x].black=ApplyEvaluateOperator(random_info[id],
661 GetPixelBlack(next,p),i == 0 ? AddEvaluateOperator : op,
662 evaluate_pixel[x].black);
663 evaluate_pixel[x].alpha=ApplyEvaluateOperator(random_info[id],
664 GetPixelAlpha(next,p),i == 0 ? AddEvaluateOperator : op,
665 evaluate_pixel[x].alpha);
666 p+=GetPixelChannels(next);
cristy08a3d702010-11-28 01:57:36 +0000667 }
668 image_view=DestroyCacheView(image_view);
669 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000670 }
cristy08a3d702010-11-28 01:57:36 +0000671 if (op == MeanEvaluateOperator)
672 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
673 {
674 evaluate_pixel[x].red/=number_images;
675 evaluate_pixel[x].green/=number_images;
676 evaluate_pixel[x].blue/=number_images;
cristy4c08aed2011-07-01 19:47:50 +0000677 evaluate_pixel[x].black/=number_images;
678 evaluate_pixel[x].alpha/=number_images;
cristy08a3d702010-11-28 01:57:36 +0000679 }
680 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
681 {
cristy4c08aed2011-07-01 19:47:50 +0000682 SetPixelRed(evaluate_image,ClampToQuantum(evaluate_pixel[x].red),q);
683 SetPixelGreen(evaluate_image,ClampToQuantum(evaluate_pixel[x].green),q);
684 SetPixelBlue(evaluate_image,ClampToQuantum(evaluate_pixel[x].blue),q);
cristy08a3d702010-11-28 01:57:36 +0000685 if (evaluate_image->colorspace == CMYKColorspace)
cristy6fbfa592011-07-03 23:04:58 +0000686 SetPixelBlack(evaluate_image,ClampToQuantum(evaluate_pixel[x].black),
687 q);
cristy4c08aed2011-07-01 19:47:50 +0000688 if (evaluate_image->matte == MagickFalse)
cristy6fbfa592011-07-03 23:04:58 +0000689 SetPixelAlpha(evaluate_image,ClampToQuantum(evaluate_pixel[x].alpha),
690 q);
cristy4c08aed2011-07-01 19:47:50 +0000691 else
cristy6fbfa592011-07-03 23:04:58 +0000692 SetPixelAlpha(evaluate_image,ClampToQuantum(evaluate_pixel[x].alpha),
693 q);
cristy4c08aed2011-07-01 19:47:50 +0000694 q+=GetPixelChannels(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000695 }
696 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
697 status=MagickFalse;
698 if (images->progress_monitor != (MagickProgressMonitor) NULL)
699 {
700 MagickBooleanType
701 proceed;
702
703#if defined(MAGICKCORE_OPENMP_SUPPORT)
704 #pragma omp critical (MagickCore_EvaluateImages)
705#endif
706 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
707 evaluate_image->rows);
708 if (proceed == MagickFalse)
709 status=MagickFalse;
710 }
711 }
cristyd18ae7c2010-03-07 17:39:52 +0000712 evaluate_view=DestroyCacheView(evaluate_view);
713 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
714 random_info=DestroyRandomInfoThreadSet(random_info);
715 if (status == MagickFalse)
716 evaluate_image=DestroyImage(evaluate_image);
717 return(evaluate_image);
718}
719
cristy351842f2010-03-07 15:27:38 +0000720MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
721 const ChannelType channel,const MagickEvaluateOperator op,const double value,
722 ExceptionInfo *exception)
723{
cristy351842f2010-03-07 15:27:38 +0000724 CacheView
725 *image_view;
726
cristy351842f2010-03-07 15:27:38 +0000727 MagickBooleanType
728 status;
729
cristy5f959472010-05-27 22:19:46 +0000730 MagickOffsetType
731 progress;
732
cristy351842f2010-03-07 15:27:38 +0000733 RandomInfo
734 **restrict random_info;
735
cristy5f959472010-05-27 22:19:46 +0000736 ssize_t
737 y;
738
cristy351842f2010-03-07 15:27:38 +0000739 assert(image != (Image *) NULL);
740 assert(image->signature == MagickSignature);
741 if (image->debug != MagickFalse)
742 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
743 assert(exception != (ExceptionInfo *) NULL);
744 assert(exception->signature == MagickSignature);
745 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
746 {
747 InheritException(exception,&image->exception);
748 return(MagickFalse);
749 }
750 status=MagickTrue;
751 progress=0;
752 random_info=AcquireRandomInfoThreadSet();
753 image_view=AcquireCacheView(image);
754#if defined(MAGICKCORE_OPENMP_SUPPORT)
755 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
756#endif
cristybb503372010-05-27 20:51:26 +0000757 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000758 {
cristy5c9e6f22010-09-17 17:31:01 +0000759 const int
760 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000761
cristy4c08aed2011-07-01 19:47:50 +0000762 register Quantum
cristy351842f2010-03-07 15:27:38 +0000763 *restrict q;
764
cristy5c9e6f22010-09-17 17:31:01 +0000765 register ssize_t
766 x;
767
cristy351842f2010-03-07 15:27:38 +0000768 if (status == MagickFalse)
769 continue;
770 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000771 if (q == (const Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +0000772 {
773 status=MagickFalse;
774 continue;
775 }
cristybb503372010-05-27 20:51:26 +0000776 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000777 {
778 if ((channel & RedChannel) != 0)
cristy6fbfa592011-07-03 23:04:58 +0000779 SetPixelRed(image,ClampToQuantum(ApplyEvaluateOperator(
780 random_info[id],GetPixelRed(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000781 if ((channel & GreenChannel) != 0)
cristy6fbfa592011-07-03 23:04:58 +0000782 SetPixelGreen(image,ClampToQuantum(ApplyEvaluateOperator(
783 random_info[id],GetPixelGreen(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000784 if ((channel & BlueChannel) != 0)
cristy6fbfa592011-07-03 23:04:58 +0000785 SetPixelBlue(image,ClampToQuantum(ApplyEvaluateOperator(
786 random_info[id],GetPixelBlue(image,q),op,value)),q);
cristy4c08aed2011-07-01 19:47:50 +0000787 if (((channel & BlackChannel) != 0) &&
788 (image->colorspace == CMYKColorspace))
cristy6fbfa592011-07-03 23:04:58 +0000789 SetPixelBlack(image,ClampToQuantum(ApplyEvaluateOperator(
790 random_info[id],GetPixelBlack(image,q),op,value)),q);
cristy4c08aed2011-07-01 19:47:50 +0000791 if ((channel & AlphaChannel) != 0)
cristy351842f2010-03-07 15:27:38 +0000792 {
793 if (image->matte == MagickFalse)
cristy6fbfa592011-07-03 23:04:58 +0000794 SetPixelAlpha(image,ClampToQuantum(ApplyEvaluateOperator(
795 random_info[id],GetPixelAlpha(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000796 else
cristy6fbfa592011-07-03 23:04:58 +0000797 SetPixelAlpha(image,ClampToQuantum(ApplyEvaluateOperator(
798 random_info[id],GetPixelAlpha(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000799 }
cristy4c08aed2011-07-01 19:47:50 +0000800 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000801 }
802 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
803 status=MagickFalse;
804 if (image->progress_monitor != (MagickProgressMonitor) NULL)
805 {
806 MagickBooleanType
807 proceed;
808
809#if defined(MAGICKCORE_OPENMP_SUPPORT)
810 #pragma omp critical (MagickCore_EvaluateImageChannel)
811#endif
812 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
813 if (proceed == MagickFalse)
814 status=MagickFalse;
815 }
816 }
817 image_view=DestroyCacheView(image_view);
818 random_info=DestroyRandomInfoThreadSet(random_info);
819 return(status);
820}
821
822/*
823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824% %
825% %
826% %
827% F u n c t i o n I m a g e %
828% %
829% %
830% %
831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832%
833% FunctionImage() applies a value to the image with an arithmetic, relational,
834% or logical operator to an image. Use these operations to lighten or darken
835% an image, to increase or decrease contrast in an image, or to produce the
836% "negative" of an image.
837%
838% The format of the FunctionImageChannel method is:
839%
840% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000841% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000842% const double *parameters,ExceptionInfo *exception)
843% MagickBooleanType FunctionImageChannel(Image *image,
844% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000845% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000846% ExceptionInfo *exception)
847%
848% A description of each parameter follows:
849%
850% o image: the image.
851%
852% o channel: the channel.
853%
854% o function: A channel function.
855%
856% o parameters: one or more parameters.
857%
858% o exception: return any errors or warnings in this structure.
859%
860*/
861
862static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000863 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000864 ExceptionInfo *exception)
865{
866 MagickRealType
867 result;
868
cristybb503372010-05-27 20:51:26 +0000869 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000870 i;
871
872 (void) exception;
873 result=0.0;
874 switch (function)
875 {
876 case PolynomialFunction:
877 {
878 /*
879 * Polynomial
880 * Parameters: polynomial constants, highest to lowest order
881 * For example: c0*x^3 + c1*x^2 + c2*x + c3
882 */
883 result=0.0;
cristybb503372010-05-27 20:51:26 +0000884 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000885 result = result*QuantumScale*pixel + parameters[i];
886 result *= QuantumRange;
887 break;
888 }
889 case SinusoidFunction:
890 {
891 /* Sinusoid Function
892 * Parameters: Freq, Phase, Ampl, bias
893 */
894 double freq,phase,ampl,bias;
895 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
896 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
897 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
898 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
899 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
900 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
901 break;
902 }
903 case ArcsinFunction:
904 {
905 /* Arcsin Function (peged at range limits for invalid results)
906 * Parameters: Width, Center, Range, Bias
907 */
908 double width,range,center,bias;
909 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
910 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
911 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
912 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
913 result = 2.0/width*(QuantumScale*pixel - center);
914 if ( result <= -1.0 )
915 result = bias - range/2.0;
916 else if ( result >= 1.0 )
917 result = bias + range/2.0;
918 else
cristy7a07f9f2010-11-27 19:09:51 +0000919 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
cristy351842f2010-03-07 15:27:38 +0000920 result *= QuantumRange;
921 break;
922 }
923 case ArctanFunction:
924 {
925 /* Arctan Function
926 * Parameters: Slope, Center, Range, Bias
927 */
928 double slope,range,center,bias;
929 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
930 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
931 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
932 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000933 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000934 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
935 result) + bias ) );
936 break;
937 }
938 case UndefinedFunction:
939 break;
940 }
941 return(ClampToQuantum(result));
942}
943
944MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000945 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000946 const double *parameters,ExceptionInfo *exception)
947{
948 MagickBooleanType
949 status;
950
cristy9a9230e2011-04-26 14:56:14 +0000951 status=FunctionImageChannel(image,CompositeChannels,function,number_parameters,
cristy351842f2010-03-07 15:27:38 +0000952 parameters,exception);
953 return(status);
954}
955
956MagickExport MagickBooleanType FunctionImageChannel(Image *image,
957 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000958 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000959 ExceptionInfo *exception)
960{
961#define FunctionImageTag "Function/Image "
962
963 CacheView
964 *image_view;
965
cristy351842f2010-03-07 15:27:38 +0000966 MagickBooleanType
967 status;
968
cristy5f959472010-05-27 22:19:46 +0000969 MagickOffsetType
970 progress;
971
972 ssize_t
973 y;
974
cristy351842f2010-03-07 15:27:38 +0000975 assert(image != (Image *) NULL);
976 assert(image->signature == MagickSignature);
977 if (image->debug != MagickFalse)
978 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
979 assert(exception != (ExceptionInfo *) NULL);
980 assert(exception->signature == MagickSignature);
981 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
982 {
983 InheritException(exception,&image->exception);
984 return(MagickFalse);
985 }
986 status=MagickTrue;
987 progress=0;
988 image_view=AcquireCacheView(image);
989#if defined(MAGICKCORE_OPENMP_SUPPORT)
990 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
991#endif
cristybb503372010-05-27 20:51:26 +0000992 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000993 {
cristybb503372010-05-27 20:51:26 +0000994 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000995 x;
996
cristy4c08aed2011-07-01 19:47:50 +0000997 register Quantum
cristy351842f2010-03-07 15:27:38 +0000998 *restrict q;
999
1000 if (status == MagickFalse)
1001 continue;
1002 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001003 if (q == (const Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +00001004 {
1005 status=MagickFalse;
1006 continue;
1007 }
cristybb503372010-05-27 20:51:26 +00001008 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001009 {
1010 if ((channel & RedChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001011 SetPixelRed(image,ApplyFunction(GetPixelRed(image,q),function,
1012 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001013 if ((channel & GreenChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001014 SetPixelGreen(image,ApplyFunction(GetPixelGreen(image,q),function,
1015 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001016 if ((channel & BlueChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001017 SetPixelBlue(image,ApplyFunction(GetPixelBlue(image,q),function,
1018 number_parameters,parameters,exception),q);
1019 if (((channel & BlackChannel) != 0) &&
1020 (image->colorspace == CMYKColorspace))
1021 SetPixelBlack(image,ApplyFunction(GetPixelBlack(image,q),function,
1022 number_parameters,parameters,exception),q);
1023 if ((channel & AlphaChannel) != 0)
cristy351842f2010-03-07 15:27:38 +00001024 {
1025 if (image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001026 SetPixelAlpha(image,ApplyFunction(GetPixelAlpha(image,q),function,
1027 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001028 else
cristy4c08aed2011-07-01 19:47:50 +00001029 SetPixelAlpha(image,ApplyFunction(GetPixelAlpha(image,q),function,
1030 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001031 }
cristy4c08aed2011-07-01 19:47:50 +00001032 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +00001033 }
1034 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1035 status=MagickFalse;
1036 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1037 {
1038 MagickBooleanType
1039 proceed;
1040
1041#if defined(MAGICKCORE_OPENMP_SUPPORT)
1042 #pragma omp critical (MagickCore_FunctionImageChannel)
1043#endif
1044 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1045 if (proceed == MagickFalse)
1046 status=MagickFalse;
1047 }
1048 }
1049 image_view=DestroyCacheView(image_view);
1050 return(status);
1051}
1052
1053/*
1054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1055% %
1056% %
1057% %
cristy3ed852e2009-09-05 21:47:34 +00001058+ G e t I m a g e C h a n n e l E x t r e m a %
1059% %
1060% %
1061% %
1062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1063%
1064% GetImageChannelExtrema() returns the extrema of one or more image channels.
1065%
1066% The format of the GetImageChannelExtrema method is:
1067%
1068% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001069% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001070% ExceptionInfo *exception)
1071%
1072% A description of each parameter follows:
1073%
1074% o image: the image.
1075%
1076% o channel: the channel.
1077%
1078% o minima: the minimum value in the channel.
1079%
1080% o maxima: the maximum value in the channel.
1081%
1082% o exception: return any errors or warnings in this structure.
1083%
1084*/
1085
1086MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001087 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001088{
cristy9a9230e2011-04-26 14:56:14 +00001089 return(GetImageChannelExtrema(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001090}
1091
1092MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001093 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001094 ExceptionInfo *exception)
1095{
1096 double
1097 max,
1098 min;
1099
1100 MagickBooleanType
1101 status;
1102
1103 assert(image != (Image *) NULL);
1104 assert(image->signature == MagickSignature);
1105 if (image->debug != MagickFalse)
1106 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1107 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001108 *minima=(size_t) ceil(min-0.5);
1109 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001110 return(status);
1111}
1112
1113/*
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115% %
1116% %
1117% %
1118% G e t I m a g e C h a n n e l M e a n %
1119% %
1120% %
1121% %
1122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123%
1124% GetImageChannelMean() returns the mean and standard deviation of one or more
1125% image channels.
1126%
1127% The format of the GetImageChannelMean method is:
1128%
1129% MagickBooleanType GetImageChannelMean(const Image *image,
1130% const ChannelType channel,double *mean,double *standard_deviation,
1131% ExceptionInfo *exception)
1132%
1133% A description of each parameter follows:
1134%
1135% o image: the image.
1136%
1137% o channel: the channel.
1138%
1139% o mean: the average value in the channel.
1140%
1141% o standard_deviation: the standard deviation of the channel.
1142%
1143% o exception: return any errors or warnings in this structure.
1144%
1145*/
1146
1147MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1148 double *standard_deviation,ExceptionInfo *exception)
1149{
1150 MagickBooleanType
1151 status;
1152
cristy9a9230e2011-04-26 14:56:14 +00001153 status=GetImageChannelMean(image,CompositeChannels,mean,standard_deviation,
cristy3ed852e2009-09-05 21:47:34 +00001154 exception);
1155 return(status);
1156}
1157
1158MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1159 const ChannelType channel,double *mean,double *standard_deviation,
1160 ExceptionInfo *exception)
1161{
cristyfd9dcd42010-08-08 18:07:02 +00001162 ChannelStatistics
1163 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001164
cristyfd9dcd42010-08-08 18:07:02 +00001165 size_t
1166 channels;
cristy3ed852e2009-09-05 21:47:34 +00001167
1168 assert(image != (Image *) NULL);
1169 assert(image->signature == MagickSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001172 channel_statistics=GetImageChannelStatistics(image,exception);
1173 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001174 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001175 channels=0;
cristy9a9230e2011-04-26 14:56:14 +00001176 channel_statistics[CompositeChannels].mean=0.0;
1177 channel_statistics[CompositeChannels].standard_deviation=0.0;
cristyfd9dcd42010-08-08 18:07:02 +00001178 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001179 {
cristy9a9230e2011-04-26 14:56:14 +00001180 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001181 channel_statistics[RedChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001182 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001183 channel_statistics[RedChannel].variance-
1184 channel_statistics[RedChannel].mean*
1185 channel_statistics[RedChannel].mean;
1186 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001187 }
cristyfd9dcd42010-08-08 18:07:02 +00001188 if ((channel & GreenChannel) != 0)
1189 {
cristy9a9230e2011-04-26 14:56:14 +00001190 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001191 channel_statistics[GreenChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001192 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001193 channel_statistics[GreenChannel].variance-
1194 channel_statistics[GreenChannel].mean*
1195 channel_statistics[GreenChannel].mean;
1196 channels++;
1197 }
1198 if ((channel & BlueChannel) != 0)
1199 {
cristy9a9230e2011-04-26 14:56:14 +00001200 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001201 channel_statistics[BlueChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001202 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001203 channel_statistics[BlueChannel].variance-
1204 channel_statistics[BlueChannel].mean*
1205 channel_statistics[BlueChannel].mean;
1206 channels++;
1207 }
cristy4c08aed2011-07-01 19:47:50 +00001208 if (((channel & BlackChannel) != 0) &&
cristyfd9dcd42010-08-08 18:07:02 +00001209 (image->colorspace == CMYKColorspace))
1210 {
cristy9a9230e2011-04-26 14:56:14 +00001211 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001212 channel_statistics[BlackChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001213 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001214 channel_statistics[BlackChannel].variance-
1215 channel_statistics[BlackChannel].mean*
1216 channel_statistics[BlackChannel].mean;
1217 channels++;
1218 }
cristy4c08aed2011-07-01 19:47:50 +00001219 if (((channel & AlphaChannel) != 0) &&
1220 (image->matte != MagickFalse))
1221 {
1222 channel_statistics[CompositeChannels].mean+=
1223 channel_statistics[AlphaChannel].mean;
1224 channel_statistics[CompositeChannels].standard_deviation+=
1225 channel_statistics[AlphaChannel].variance-
1226 channel_statistics[AlphaChannel].mean*
1227 channel_statistics[AlphaChannel].mean;
1228 channels++;
1229 }
cristy9a9230e2011-04-26 14:56:14 +00001230 channel_statistics[CompositeChannels].mean/=channels;
1231 channel_statistics[CompositeChannels].standard_deviation=
1232 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1233 *mean=channel_statistics[CompositeChannels].mean;
1234 *standard_deviation=channel_statistics[CompositeChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001235 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1236 channel_statistics);
1237 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001238}
1239
1240/*
1241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242% %
1243% %
1244% %
1245% G e t I m a g e C h a n n e l K u r t o s i s %
1246% %
1247% %
1248% %
1249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250%
1251% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1252% image channels.
1253%
1254% The format of the GetImageChannelKurtosis method is:
1255%
1256% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1257% const ChannelType channel,double *kurtosis,double *skewness,
1258% ExceptionInfo *exception)
1259%
1260% A description of each parameter follows:
1261%
1262% o image: the image.
1263%
1264% o channel: the channel.
1265%
1266% o kurtosis: the kurtosis of the channel.
1267%
1268% o skewness: the skewness of the channel.
1269%
1270% o exception: return any errors or warnings in this structure.
1271%
1272*/
1273
1274MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1275 double *kurtosis,double *skewness,ExceptionInfo *exception)
1276{
1277 MagickBooleanType
1278 status;
1279
cristy9a9230e2011-04-26 14:56:14 +00001280 status=GetImageChannelKurtosis(image,CompositeChannels,kurtosis,skewness,
cristy3ed852e2009-09-05 21:47:34 +00001281 exception);
1282 return(status);
1283}
1284
1285MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1286 const ChannelType channel,double *kurtosis,double *skewness,
1287 ExceptionInfo *exception)
1288{
1289 double
1290 area,
1291 mean,
1292 standard_deviation,
1293 sum_squares,
1294 sum_cubes,
1295 sum_fourth_power;
1296
cristybb503372010-05-27 20:51:26 +00001297 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001298 y;
1299
1300 assert(image != (Image *) NULL);
1301 assert(image->signature == MagickSignature);
1302 if (image->debug != MagickFalse)
1303 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1304 *kurtosis=0.0;
1305 *skewness=0.0;
1306 area=0.0;
1307 mean=0.0;
1308 standard_deviation=0.0;
1309 sum_squares=0.0;
1310 sum_cubes=0.0;
1311 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001312 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001313 {
cristy4c08aed2011-07-01 19:47:50 +00001314 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001315 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001316
cristybb503372010-05-27 20:51:26 +00001317 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001318 x;
1319
1320 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001321 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001322 break;
cristybb503372010-05-27 20:51:26 +00001323 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001324 {
1325 if ((channel & RedChannel) != 0)
1326 {
cristy4c08aed2011-07-01 19:47:50 +00001327 mean+=GetPixelRed(image,p);
1328 sum_squares+=(double) GetPixelRed(image,p)*GetPixelRed(image,p);
1329 sum_cubes+=(double) GetPixelRed(image,p)*GetPixelRed(image,p)*
1330 GetPixelRed(image,p);
1331 sum_fourth_power+=(double) GetPixelRed(image,p)*
1332 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001333 area++;
1334 }
1335 if ((channel & GreenChannel) != 0)
1336 {
cristy4c08aed2011-07-01 19:47:50 +00001337 mean+=GetPixelGreen(image,p);
1338 sum_squares+=(double) GetPixelGreen(image,p)*GetPixelGreen(image,p);
1339 sum_cubes+=(double) GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1340 GetPixelGreen(image,p);
1341 sum_fourth_power+=(double) GetPixelGreen(image,p)*
1342 GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1343 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001344 area++;
1345 }
1346 if ((channel & BlueChannel) != 0)
1347 {
cristy4c08aed2011-07-01 19:47:50 +00001348 mean+=GetPixelBlue(image,p);
1349 sum_squares+=(double) GetPixelBlue(image,p)*GetPixelBlue(image,p);
1350 sum_cubes+=(double) GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1351 GetPixelBlue(image,p);
1352 sum_fourth_power+=(double) GetPixelBlue(image,p)*
1353 GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1354 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001355 area++;
1356 }
cristy4c08aed2011-07-01 19:47:50 +00001357 if (((channel & BlackChannel) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001358 (image->colorspace == CMYKColorspace))
1359 {
cristy4c08aed2011-07-01 19:47:50 +00001360 mean+=GetPixelBlack(image,p);
1361 sum_squares+=(double) GetPixelBlack(image,p)*GetPixelBlack(image,p);
1362 sum_cubes+=(double) GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1363 GetPixelBlack(image,p);
1364 sum_fourth_power+=(double) GetPixelBlack(image,p)*
1365 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1366 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001367 area++;
1368 }
cristy4c08aed2011-07-01 19:47:50 +00001369 if ((channel & AlphaChannel) != 0)
1370 {
1371 mean+=GetPixelAlpha(image,p);
1372 sum_squares+=(double) GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
1373 sum_cubes+=(double) GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1374 GetPixelAlpha(image,p);
1375 sum_fourth_power+=(double) GetPixelAlpha(image,p)*
1376 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1377 GetPixelAlpha(image,p);
1378 area++;
1379 }
1380 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001381 }
1382 }
cristybb503372010-05-27 20:51:26 +00001383 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001384 return(MagickFalse);
1385 if (area != 0.0)
1386 {
1387 mean/=area;
1388 sum_squares/=area;
1389 sum_cubes/=area;
1390 sum_fourth_power/=area;
1391 }
1392 standard_deviation=sqrt(sum_squares-(mean*mean));
1393 if (standard_deviation != 0.0)
1394 {
1395 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1396 3.0*mean*mean*mean*mean;
1397 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1398 standard_deviation;
1399 *kurtosis-=3.0;
1400 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1401 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1402 }
cristybb503372010-05-27 20:51:26 +00001403 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001404}
cristy46f08202010-01-10 04:04:21 +00001405
cristy3ed852e2009-09-05 21:47:34 +00001406/*
1407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408% %
1409% %
1410% %
1411% G e t I m a g e C h a n n e l R a n g e %
1412% %
1413% %
1414% %
1415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1416%
1417% GetImageChannelRange() returns the range of one or more image channels.
1418%
1419% The format of the GetImageChannelRange method is:
1420%
1421% MagickBooleanType GetImageChannelRange(const Image *image,
1422% const ChannelType channel,double *minima,double *maxima,
1423% ExceptionInfo *exception)
1424%
1425% A description of each parameter follows:
1426%
1427% o image: the image.
1428%
1429% o channel: the channel.
1430%
1431% o minima: the minimum value in the channel.
1432%
1433% o maxima: the maximum value in the channel.
1434%
1435% o exception: return any errors or warnings in this structure.
1436%
1437*/
1438
1439MagickExport MagickBooleanType GetImageRange(const Image *image,
1440 double *minima,double *maxima,ExceptionInfo *exception)
1441{
cristy9a9230e2011-04-26 14:56:14 +00001442 return(GetImageChannelRange(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001443}
1444
1445MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1446 const ChannelType channel,double *minima,double *maxima,
1447 ExceptionInfo *exception)
1448{
cristy4c08aed2011-07-01 19:47:50 +00001449 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001450 pixel;
1451
cristy9d314ff2011-03-09 01:30:28 +00001452 ssize_t
1453 y;
1454
cristy3ed852e2009-09-05 21:47:34 +00001455 assert(image != (Image *) NULL);
1456 assert(image->signature == MagickSignature);
1457 if (image->debug != MagickFalse)
1458 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1459 *maxima=(-1.0E-37);
1460 *minima=1.0E+37;
cristy4c08aed2011-07-01 19:47:50 +00001461 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001462 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001463 {
cristy4c08aed2011-07-01 19:47:50 +00001464 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001465 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001466
cristybb503372010-05-27 20:51:26 +00001467 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001468 x;
1469
1470 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001471 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001472 break;
cristybb503372010-05-27 20:51:26 +00001473 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001474 {
cristy4c08aed2011-07-01 19:47:50 +00001475 SetPixelInfo(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00001476 if ((channel & RedChannel) != 0)
1477 {
1478 if (pixel.red < *minima)
1479 *minima=(double) pixel.red;
1480 if (pixel.red > *maxima)
1481 *maxima=(double) pixel.red;
1482 }
1483 if ((channel & GreenChannel) != 0)
1484 {
1485 if (pixel.green < *minima)
1486 *minima=(double) pixel.green;
1487 if (pixel.green > *maxima)
1488 *maxima=(double) pixel.green;
1489 }
1490 if ((channel & BlueChannel) != 0)
1491 {
1492 if (pixel.blue < *minima)
1493 *minima=(double) pixel.blue;
1494 if (pixel.blue > *maxima)
1495 *maxima=(double) pixel.blue;
1496 }
cristy4c08aed2011-07-01 19:47:50 +00001497 if (((channel & BlackChannel) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001498 (image->colorspace == CMYKColorspace))
1499 {
cristy4c08aed2011-07-01 19:47:50 +00001500 if (pixel.black < *minima)
1501 *minima=(double) pixel.black;
1502 if (pixel.black > *maxima)
1503 *maxima=(double) pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00001504 }
cristy4c08aed2011-07-01 19:47:50 +00001505 if ((channel & AlphaChannel) != 0)
1506 {
1507 if (pixel.alpha < *minima)
1508 *minima=(double) pixel.alpha;
1509 if (pixel.alpha > *maxima)
1510 *maxima=(double) pixel.alpha;
1511 }
1512 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001513 }
1514 }
cristybb503372010-05-27 20:51:26 +00001515 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001516}
1517
1518/*
1519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1520% %
1521% %
1522% %
1523% 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 %
1524% %
1525% %
1526% %
1527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1528%
1529% GetImageChannelStatistics() returns statistics for each channel in the
1530% image. The statistics include the channel depth, its minima, maxima, mean,
1531% standard deviation, kurtosis and skewness. You can access the red channel
1532% mean, for example, like this:
1533%
cristy0c1c3fd2011-01-18 15:53:10 +00001534% channel_statistics=GetImageChannelStatistics(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001535% red_mean=channel_statistics[RedChannel].mean;
1536%
1537% Use MagickRelinquishMemory() to free the statistics buffer.
1538%
1539% The format of the GetImageChannelStatistics method is:
1540%
1541% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1542% ExceptionInfo *exception)
1543%
1544% A description of each parameter follows:
1545%
1546% o image: the image.
1547%
1548% o exception: return any errors or warnings in this structure.
1549%
1550*/
cristy3ed852e2009-09-05 21:47:34 +00001551MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1552 ExceptionInfo *exception)
1553{
1554 ChannelStatistics
1555 *channel_statistics;
1556
1557 double
cristyfd9dcd42010-08-08 18:07:02 +00001558 area;
cristy3ed852e2009-09-05 21:47:34 +00001559
cristy3ed852e2009-09-05 21:47:34 +00001560 MagickStatusType
1561 status;
1562
1563 QuantumAny
1564 range;
1565
cristybb503372010-05-27 20:51:26 +00001566 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001567 i;
1568
1569 size_t
cristy9d314ff2011-03-09 01:30:28 +00001570 channels,
1571 depth,
cristy3ed852e2009-09-05 21:47:34 +00001572 length;
1573
cristy9d314ff2011-03-09 01:30:28 +00001574 ssize_t
1575 y;
cristy3ed852e2009-09-05 21:47:34 +00001576
1577 assert(image != (Image *) NULL);
1578 assert(image->signature == MagickSignature);
1579 if (image->debug != MagickFalse)
1580 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy9a9230e2011-04-26 14:56:14 +00001581 length=CompositeChannels+1UL;
cristy3ed852e2009-09-05 21:47:34 +00001582 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1583 sizeof(*channel_statistics));
1584 if (channel_statistics == (ChannelStatistics *) NULL)
1585 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1586 (void) ResetMagickMemory(channel_statistics,0,length*
1587 sizeof(*channel_statistics));
cristy32daba42011-05-01 02:34:39 +00001588 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 channel_statistics[i].depth=1;
1591 channel_statistics[i].maxima=(-1.0E-37);
1592 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001593 }
cristybb503372010-05-27 20:51:26 +00001594 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001595 {
cristy4c08aed2011-07-01 19:47:50 +00001596 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001597 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001598
cristybb503372010-05-27 20:51:26 +00001599 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001600 x;
1601
1602 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001603 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001604 break;
cristybb503372010-05-27 20:51:26 +00001605 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001606 {
1607 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1608 {
1609 depth=channel_statistics[RedChannel].depth;
1610 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001611 status=GetPixelRed(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1612 GetPixelRed(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001613 if (status != MagickFalse)
1614 {
1615 channel_statistics[RedChannel].depth++;
1616 continue;
1617 }
1618 }
1619 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1620 {
1621 depth=channel_statistics[GreenChannel].depth;
1622 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001623 status=GetPixelGreen(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1624 GetPixelGreen(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001625 if (status != MagickFalse)
1626 {
1627 channel_statistics[GreenChannel].depth++;
1628 continue;
1629 }
1630 }
1631 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1632 {
1633 depth=channel_statistics[BlueChannel].depth;
1634 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001635 status=GetPixelBlue(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1636 GetPixelBlue(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001637 if (status != MagickFalse)
1638 {
1639 channel_statistics[BlueChannel].depth++;
1640 continue;
1641 }
1642 }
1643 if (image->matte != MagickFalse)
1644 {
cristy4c08aed2011-07-01 19:47:50 +00001645 if (channel_statistics[AlphaChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
cristy4c08aed2011-07-01 19:47:50 +00001647 depth=channel_statistics[AlphaChannel].depth;
cristy3ed852e2009-09-05 21:47:34 +00001648 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001649 status=GetPixelAlpha(image,p) != ScaleAnyToQuantum(
1650 ScaleQuantumToAny(GetPixelAlpha(image,p),range),range) ?
cristy32daba42011-05-01 02:34:39 +00001651 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001652 if (status != MagickFalse)
1653 {
cristy4c08aed2011-07-01 19:47:50 +00001654 channel_statistics[AlphaChannel].depth++;
cristy3ed852e2009-09-05 21:47:34 +00001655 continue;
1656 }
1657 }
1658 }
1659 if (image->colorspace == CMYKColorspace)
1660 {
1661 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1662 {
1663 depth=channel_statistics[BlackChannel].depth;
1664 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001665 status=GetPixelBlack(image,p) != ScaleAnyToQuantum(
1666 ScaleQuantumToAny(GetPixelBlack(image,p),range),range) ?
1667 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001668 if (status != MagickFalse)
1669 {
1670 channel_statistics[BlackChannel].depth++;
1671 continue;
1672 }
1673 }
1674 }
cristy4c08aed2011-07-01 19:47:50 +00001675 if ((double) GetPixelRed(image,p) < channel_statistics[RedChannel].minima)
1676 channel_statistics[RedChannel].minima=(double) GetPixelRed(image,p);
1677 if ((double) GetPixelRed(image,p) > channel_statistics[RedChannel].maxima)
1678 channel_statistics[RedChannel].maxima=(double) GetPixelRed(image,p);
1679 channel_statistics[RedChannel].sum+=GetPixelRed(image,p);
1680 channel_statistics[RedChannel].sum_squared+=(double)
1681 GetPixelRed(image,p)*GetPixelRed(image,p);
cristy32daba42011-05-01 02:34:39 +00001682 channel_statistics[RedChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001683 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p);
cristy32daba42011-05-01 02:34:39 +00001684 channel_statistics[RedChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001685 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p)*
1686 GetPixelRed(image,p);
1687 if ((double) GetPixelGreen(image,p) < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001688 channel_statistics[GreenChannel].minima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001689 GetPixelGreen(image,p);
1690 if ((double) GetPixelGreen(image,p) > channel_statistics[GreenChannel].maxima)
1691 channel_statistics[GreenChannel].maxima=(double) GetPixelGreen(image,p);
1692 channel_statistics[GreenChannel].sum+=GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001693 channel_statistics[GreenChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001694 GetPixelGreen(image,p)*GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001695 channel_statistics[GreenChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001696 GetPixelGreen(image,p)*GetPixelGreen(image,p)*GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001697 channel_statistics[GreenChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001698 GetPixelGreen(image,p)*GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1699 GetPixelGreen(image,p);
1700 if ((double) GetPixelBlue(image,p) < channel_statistics[BlueChannel].minima)
1701 channel_statistics[BlueChannel].minima=(double) GetPixelBlue(image,p);
1702 if ((double) GetPixelBlue(image,p) > channel_statistics[BlueChannel].maxima)
1703 channel_statistics[BlueChannel].maxima=(double) GetPixelBlue(image,p);
1704 channel_statistics[BlueChannel].sum+=GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001705 channel_statistics[BlueChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001706 GetPixelBlue(image,p)*GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001707 channel_statistics[BlueChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001708 GetPixelBlue(image,p)*GetPixelBlue(image,p)*GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001709 channel_statistics[BlueChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001710 GetPixelBlue(image,p)*GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1711 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001712 if (image->colorspace == CMYKColorspace)
1713 {
cristy4c08aed2011-07-01 19:47:50 +00001714 if ((double) GetPixelBlack(image,p) < channel_statistics[BlackChannel].minima)
cristyfba5a8b2011-05-03 17:12:12 +00001715 channel_statistics[BlackChannel].minima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001716 GetPixelBlack(image,p);
1717 if ((double) GetPixelBlack(image,p) > channel_statistics[BlackChannel].maxima)
cristyfba5a8b2011-05-03 17:12:12 +00001718 channel_statistics[BlackChannel].maxima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001719 GetPixelBlack(image,p);
1720 channel_statistics[BlackChannel].sum+=GetPixelBlack(image,p);
cristyfd9dcd42010-08-08 18:07:02 +00001721 channel_statistics[BlackChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001722 GetPixelBlack(image,p)*GetPixelBlack(image,p);
cristyfba5a8b2011-05-03 17:12:12 +00001723 channel_statistics[BlackChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001724 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1725 GetPixelBlack(image,p);
cristya8178ed2010-08-10 17:31:59 +00001726 channel_statistics[BlackChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001727 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1728 GetPixelBlack(image,p)*GetPixelBlack(image,p);
1729 }
1730 if (image->matte != MagickFalse)
1731 {
1732 if ((double) GetPixelAlpha(image,p) < channel_statistics[AlphaChannel].minima)
1733 channel_statistics[AlphaChannel].minima=(double)
1734 GetPixelAlpha(image,p);
1735 if ((double) GetPixelAlpha(image,p) > channel_statistics[AlphaChannel].maxima)
1736 channel_statistics[AlphaChannel].maxima=(double)
1737 GetPixelAlpha(image,p);
1738 channel_statistics[AlphaChannel].sum+=GetPixelAlpha(image,p);
1739 channel_statistics[AlphaChannel].sum_squared+=(double)
1740 GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
1741 channel_statistics[AlphaChannel].sum_cubed+=(double)
1742 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1743 GetPixelAlpha(image,p);
1744 channel_statistics[AlphaChannel].sum_fourth_power+=(double)
1745 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1746 GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001747 }
1748 x++;
cristy4c08aed2011-07-01 19:47:50 +00001749 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001750 }
1751 }
1752 area=(double) image->columns*image->rows;
cristy32daba42011-05-01 02:34:39 +00001753 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001754 {
cristyfd9dcd42010-08-08 18:07:02 +00001755 channel_statistics[i].sum/=area;
1756 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001757 channel_statistics[i].sum_cubed/=area;
1758 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001759 channel_statistics[i].mean=channel_statistics[i].sum;
1760 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1761 channel_statistics[i].standard_deviation=sqrt(
1762 channel_statistics[i].variance-(channel_statistics[i].mean*
1763 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001764 }
cristy32daba42011-05-01 02:34:39 +00001765 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001766 {
cristy9a9230e2011-04-26 14:56:14 +00001767 channel_statistics[CompositeChannels].depth=(size_t) MagickMax((double)
1768 channel_statistics[CompositeChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001769 channel_statistics[i].depth);
cristy9a9230e2011-04-26 14:56:14 +00001770 channel_statistics[CompositeChannels].minima=MagickMin(
cristy32daba42011-05-01 02:34:39 +00001771 channel_statistics[CompositeChannels].minima,
1772 channel_statistics[i].minima);
cristy9a9230e2011-04-26 14:56:14 +00001773 channel_statistics[CompositeChannels].maxima=MagickMax(
cristy32daba42011-05-01 02:34:39 +00001774 channel_statistics[CompositeChannels].maxima,
1775 channel_statistics[i].maxima);
cristy9a9230e2011-04-26 14:56:14 +00001776 channel_statistics[CompositeChannels].sum+=channel_statistics[i].sum;
1777 channel_statistics[CompositeChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001778 channel_statistics[i].sum_squared;
cristy32daba42011-05-01 02:34:39 +00001779 channel_statistics[CompositeChannels].sum_cubed+=
1780 channel_statistics[i].sum_cubed;
cristy9a9230e2011-04-26 14:56:14 +00001781 channel_statistics[CompositeChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001782 channel_statistics[i].sum_fourth_power;
cristy9a9230e2011-04-26 14:56:14 +00001783 channel_statistics[CompositeChannels].mean+=channel_statistics[i].mean;
cristy32daba42011-05-01 02:34:39 +00001784 channel_statistics[CompositeChannels].variance+=
1785 channel_statistics[i].variance-channel_statistics[i].mean*
1786 channel_statistics[i].mean;
cristy9a9230e2011-04-26 14:56:14 +00001787 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001788 channel_statistics[i].variance-channel_statistics[i].mean*
1789 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001790 }
cristycf584452010-08-08 02:26:04 +00001791 channels=3;
1792 if (image->matte != MagickFalse)
1793 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001794 if (image->colorspace == CMYKColorspace)
1795 channels++;
cristy9a9230e2011-04-26 14:56:14 +00001796 channel_statistics[CompositeChannels].sum/=channels;
1797 channel_statistics[CompositeChannels].sum_squared/=channels;
1798 channel_statistics[CompositeChannels].sum_cubed/=channels;
1799 channel_statistics[CompositeChannels].sum_fourth_power/=channels;
1800 channel_statistics[CompositeChannels].mean/=channels;
1801 channel_statistics[CompositeChannels].variance/=channels;
1802 channel_statistics[CompositeChannels].standard_deviation=
1803 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1804 channel_statistics[CompositeChannels].kurtosis/=channels;
1805 channel_statistics[CompositeChannels].skewness/=channels;
cristy32daba42011-05-01 02:34:39 +00001806 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001807 {
cristy3ed852e2009-09-05 21:47:34 +00001808 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001809 continue;
1810 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1811 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1812 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1813 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1814 channel_statistics[i].standard_deviation*
1815 channel_statistics[i].standard_deviation);
1816 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1817 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1818 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1819 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1820 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1821 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1822 channel_statistics[i].standard_deviation*
1823 channel_statistics[i].standard_deviation*
1824 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001825 }
1826 return(channel_statistics);
1827}