blob: 905435489766d339db925886166df626124381f6 [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)
cristy4c08aed2011-07-01 19:47:50 +0000686 SetPixelBlack(evaluate_image,
687 ClampToQuantum(evaluate_pixel[x].black),q);
688 if (evaluate_image->matte == MagickFalse)
689 SetPixelAlpha(evaluate_image,
690 ClampToQuantum(evaluate_pixel[x].alpha),q);
691 else
692 SetPixelAlpha(evaluate_image,
693 ClampToQuantum(evaluate_pixel[x].alpha),q);
694 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)
cristy4c08aed2011-07-01 19:47:50 +0000779 SetPixelRed(image,ClampToQuantum(
780 ApplyEvaluateOperator(random_info[id],
781 GetPixelRed(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000782 if ((channel & GreenChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000783 SetPixelGreen(image,ClampToQuantum(
784 ApplyEvaluateOperator(random_info[id],
785 GetPixelGreen(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000786 if ((channel & BlueChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000787 SetPixelBlue(image,ClampToQuantum(
788 ApplyEvaluateOperator(random_info[id],
789 GetPixelBlue(image,q),op,value)),q);
790 if (((channel & BlackChannel) != 0) &&
791 (image->colorspace == CMYKColorspace))
792 SetPixelBlack(image,ClampToQuantum(
793 ApplyEvaluateOperator(random_info[id],
794 GetPixelBlack(image,q),op,value)),q);
795 if ((channel & AlphaChannel) != 0)
cristy351842f2010-03-07 15:27:38 +0000796 {
797 if (image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000798 SetPixelAlpha(image,
799 ClampToQuantum(ApplyEvaluateOperator(random_info[id],
800 GetPixelAlpha(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000801 else
cristy4c08aed2011-07-01 19:47:50 +0000802 SetPixelAlpha(image,
803 ClampToQuantum(ApplyEvaluateOperator(random_info[id],
804 GetPixelAlpha(image,q),op,value)),q);
cristy351842f2010-03-07 15:27:38 +0000805 }
cristy4c08aed2011-07-01 19:47:50 +0000806 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000807 }
808 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
809 status=MagickFalse;
810 if (image->progress_monitor != (MagickProgressMonitor) NULL)
811 {
812 MagickBooleanType
813 proceed;
814
815#if defined(MAGICKCORE_OPENMP_SUPPORT)
816 #pragma omp critical (MagickCore_EvaluateImageChannel)
817#endif
818 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
819 if (proceed == MagickFalse)
820 status=MagickFalse;
821 }
822 }
823 image_view=DestroyCacheView(image_view);
824 random_info=DestroyRandomInfoThreadSet(random_info);
825 return(status);
826}
827
828/*
829%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
830% %
831% %
832% %
833% F u n c t i o n I m a g e %
834% %
835% %
836% %
837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838%
839% FunctionImage() applies a value to the image with an arithmetic, relational,
840% or logical operator to an image. Use these operations to lighten or darken
841% an image, to increase or decrease contrast in an image, or to produce the
842% "negative" of an image.
843%
844% The format of the FunctionImageChannel method is:
845%
846% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000847% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000848% const double *parameters,ExceptionInfo *exception)
849% MagickBooleanType FunctionImageChannel(Image *image,
850% const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000851% const ssize_t number_parameters,const double *argument,
cristy351842f2010-03-07 15:27:38 +0000852% ExceptionInfo *exception)
853%
854% A description of each parameter follows:
855%
856% o image: the image.
857%
858% o channel: the channel.
859%
860% o function: A channel function.
861%
862% o parameters: one or more parameters.
863%
864% o exception: return any errors or warnings in this structure.
865%
866*/
867
868static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000869 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000870 ExceptionInfo *exception)
871{
872 MagickRealType
873 result;
874
cristybb503372010-05-27 20:51:26 +0000875 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000876 i;
877
878 (void) exception;
879 result=0.0;
880 switch (function)
881 {
882 case PolynomialFunction:
883 {
884 /*
885 * Polynomial
886 * Parameters: polynomial constants, highest to lowest order
887 * For example: c0*x^3 + c1*x^2 + c2*x + c3
888 */
889 result=0.0;
cristybb503372010-05-27 20:51:26 +0000890 for (i=0; i < (ssize_t) number_parameters; i++)
cristy351842f2010-03-07 15:27:38 +0000891 result = result*QuantumScale*pixel + parameters[i];
892 result *= QuantumRange;
893 break;
894 }
895 case SinusoidFunction:
896 {
897 /* Sinusoid Function
898 * Parameters: Freq, Phase, Ampl, bias
899 */
900 double freq,phase,ampl,bias;
901 freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
902 phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
903 ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
904 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
905 result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
906 (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
907 break;
908 }
909 case ArcsinFunction:
910 {
911 /* Arcsin Function (peged at range limits for invalid results)
912 * Parameters: Width, Center, Range, Bias
913 */
914 double width,range,center,bias;
915 width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
916 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
917 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
918 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
919 result = 2.0/width*(QuantumScale*pixel - center);
920 if ( result <= -1.0 )
921 result = bias - range/2.0;
922 else if ( result >= 1.0 )
923 result = bias + range/2.0;
924 else
cristy7a07f9f2010-11-27 19:09:51 +0000925 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
cristy351842f2010-03-07 15:27:38 +0000926 result *= QuantumRange;
927 break;
928 }
929 case ArctanFunction:
930 {
931 /* Arctan Function
932 * Parameters: Slope, Center, Range, Bias
933 */
934 double slope,range,center,bias;
935 slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
936 center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
937 range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
938 bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000939 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000940 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
941 result) + bias ) );
942 break;
943 }
944 case UndefinedFunction:
945 break;
946 }
947 return(ClampToQuantum(result));
948}
949
950MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000951 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000952 const double *parameters,ExceptionInfo *exception)
953{
954 MagickBooleanType
955 status;
956
cristy9a9230e2011-04-26 14:56:14 +0000957 status=FunctionImageChannel(image,CompositeChannels,function,number_parameters,
cristy351842f2010-03-07 15:27:38 +0000958 parameters,exception);
959 return(status);
960}
961
962MagickExport MagickBooleanType FunctionImageChannel(Image *image,
963 const ChannelType channel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000964 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000965 ExceptionInfo *exception)
966{
967#define FunctionImageTag "Function/Image "
968
969 CacheView
970 *image_view;
971
cristy351842f2010-03-07 15:27:38 +0000972 MagickBooleanType
973 status;
974
cristy5f959472010-05-27 22:19:46 +0000975 MagickOffsetType
976 progress;
977
978 ssize_t
979 y;
980
cristy351842f2010-03-07 15:27:38 +0000981 assert(image != (Image *) NULL);
982 assert(image->signature == MagickSignature);
983 if (image->debug != MagickFalse)
984 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
985 assert(exception != (ExceptionInfo *) NULL);
986 assert(exception->signature == MagickSignature);
987 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
988 {
989 InheritException(exception,&image->exception);
990 return(MagickFalse);
991 }
992 status=MagickTrue;
993 progress=0;
994 image_view=AcquireCacheView(image);
995#if defined(MAGICKCORE_OPENMP_SUPPORT)
996 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
997#endif
cristybb503372010-05-27 20:51:26 +0000998 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000999 {
cristybb503372010-05-27 20:51:26 +00001000 register ssize_t
cristy351842f2010-03-07 15:27:38 +00001001 x;
1002
cristy4c08aed2011-07-01 19:47:50 +00001003 register Quantum
cristy351842f2010-03-07 15:27:38 +00001004 *restrict q;
1005
1006 if (status == MagickFalse)
1007 continue;
1008 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001009 if (q == (const Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +00001010 {
1011 status=MagickFalse;
1012 continue;
1013 }
cristybb503372010-05-27 20:51:26 +00001014 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001015 {
1016 if ((channel & RedChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001017 SetPixelRed(image,ApplyFunction(GetPixelRed(image,q),function,
1018 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001019 if ((channel & GreenChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001020 SetPixelGreen(image,ApplyFunction(GetPixelGreen(image,q),function,
1021 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001022 if ((channel & BlueChannel) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001023 SetPixelBlue(image,ApplyFunction(GetPixelBlue(image,q),function,
1024 number_parameters,parameters,exception),q);
1025 if (((channel & BlackChannel) != 0) &&
1026 (image->colorspace == CMYKColorspace))
1027 SetPixelBlack(image,ApplyFunction(GetPixelBlack(image,q),function,
1028 number_parameters,parameters,exception),q);
1029 if ((channel & AlphaChannel) != 0)
cristy351842f2010-03-07 15:27:38 +00001030 {
1031 if (image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001032 SetPixelAlpha(image,ApplyFunction(GetPixelAlpha(image,q),function,
1033 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001034 else
cristy4c08aed2011-07-01 19:47:50 +00001035 SetPixelAlpha(image,ApplyFunction(GetPixelAlpha(image,q),function,
1036 number_parameters,parameters,exception),q);
cristy351842f2010-03-07 15:27:38 +00001037 }
cristy4c08aed2011-07-01 19:47:50 +00001038 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +00001039 }
1040 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1041 status=MagickFalse;
1042 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1043 {
1044 MagickBooleanType
1045 proceed;
1046
1047#if defined(MAGICKCORE_OPENMP_SUPPORT)
1048 #pragma omp critical (MagickCore_FunctionImageChannel)
1049#endif
1050 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1051 if (proceed == MagickFalse)
1052 status=MagickFalse;
1053 }
1054 }
1055 image_view=DestroyCacheView(image_view);
1056 return(status);
1057}
1058
1059/*
1060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061% %
1062% %
1063% %
cristy3ed852e2009-09-05 21:47:34 +00001064+ G e t I m a g e C h a n n e l E x t r e m a %
1065% %
1066% %
1067% %
1068%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069%
1070% GetImageChannelExtrema() returns the extrema of one or more image channels.
1071%
1072% The format of the GetImageChannelExtrema method is:
1073%
1074% MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001075% const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001076% ExceptionInfo *exception)
1077%
1078% A description of each parameter follows:
1079%
1080% o image: the image.
1081%
1082% o channel: the channel.
1083%
1084% o minima: the minimum value in the channel.
1085%
1086% o maxima: the maximum value in the channel.
1087%
1088% o exception: return any errors or warnings in this structure.
1089%
1090*/
1091
1092MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001093 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001094{
cristy9a9230e2011-04-26 14:56:14 +00001095 return(GetImageChannelExtrema(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001096}
1097
1098MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001099 const ChannelType channel,size_t *minima,size_t *maxima,
cristy3ed852e2009-09-05 21:47:34 +00001100 ExceptionInfo *exception)
1101{
1102 double
1103 max,
1104 min;
1105
1106 MagickBooleanType
1107 status;
1108
1109 assert(image != (Image *) NULL);
1110 assert(image->signature == MagickSignature);
1111 if (image->debug != MagickFalse)
1112 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1113 status=GetImageChannelRange(image,channel,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001114 *minima=(size_t) ceil(min-0.5);
1115 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001116 return(status);
1117}
1118
1119/*
1120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1121% %
1122% %
1123% %
1124% G e t I m a g e C h a n n e l M e a n %
1125% %
1126% %
1127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129%
1130% GetImageChannelMean() returns the mean and standard deviation of one or more
1131% image channels.
1132%
1133% The format of the GetImageChannelMean method is:
1134%
1135% MagickBooleanType GetImageChannelMean(const Image *image,
1136% const ChannelType channel,double *mean,double *standard_deviation,
1137% ExceptionInfo *exception)
1138%
1139% A description of each parameter follows:
1140%
1141% o image: the image.
1142%
1143% o channel: the channel.
1144%
1145% o mean: the average value in the channel.
1146%
1147% o standard_deviation: the standard deviation of the channel.
1148%
1149% o exception: return any errors or warnings in this structure.
1150%
1151*/
1152
1153MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1154 double *standard_deviation,ExceptionInfo *exception)
1155{
1156 MagickBooleanType
1157 status;
1158
cristy9a9230e2011-04-26 14:56:14 +00001159 status=GetImageChannelMean(image,CompositeChannels,mean,standard_deviation,
cristy3ed852e2009-09-05 21:47:34 +00001160 exception);
1161 return(status);
1162}
1163
1164MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
1165 const ChannelType channel,double *mean,double *standard_deviation,
1166 ExceptionInfo *exception)
1167{
cristyfd9dcd42010-08-08 18:07:02 +00001168 ChannelStatistics
1169 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001170
cristyfd9dcd42010-08-08 18:07:02 +00001171 size_t
1172 channels;
cristy3ed852e2009-09-05 21:47:34 +00001173
1174 assert(image != (Image *) NULL);
1175 assert(image->signature == MagickSignature);
1176 if (image->debug != MagickFalse)
1177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyfd9dcd42010-08-08 18:07:02 +00001178 channel_statistics=GetImageChannelStatistics(image,exception);
1179 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001180 return(MagickFalse);
cristyfd9dcd42010-08-08 18:07:02 +00001181 channels=0;
cristy9a9230e2011-04-26 14:56:14 +00001182 channel_statistics[CompositeChannels].mean=0.0;
1183 channel_statistics[CompositeChannels].standard_deviation=0.0;
cristyfd9dcd42010-08-08 18:07:02 +00001184 if ((channel & RedChannel) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001185 {
cristy9a9230e2011-04-26 14:56:14 +00001186 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001187 channel_statistics[RedChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001188 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001189 channel_statistics[RedChannel].variance-
1190 channel_statistics[RedChannel].mean*
1191 channel_statistics[RedChannel].mean;
1192 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001193 }
cristyfd9dcd42010-08-08 18:07:02 +00001194 if ((channel & GreenChannel) != 0)
1195 {
cristy9a9230e2011-04-26 14:56:14 +00001196 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001197 channel_statistics[GreenChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001198 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001199 channel_statistics[GreenChannel].variance-
1200 channel_statistics[GreenChannel].mean*
1201 channel_statistics[GreenChannel].mean;
1202 channels++;
1203 }
1204 if ((channel & BlueChannel) != 0)
1205 {
cristy9a9230e2011-04-26 14:56:14 +00001206 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001207 channel_statistics[BlueChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001208 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001209 channel_statistics[BlueChannel].variance-
1210 channel_statistics[BlueChannel].mean*
1211 channel_statistics[BlueChannel].mean;
1212 channels++;
1213 }
cristy4c08aed2011-07-01 19:47:50 +00001214 if (((channel & BlackChannel) != 0) &&
cristyfd9dcd42010-08-08 18:07:02 +00001215 (image->colorspace == CMYKColorspace))
1216 {
cristy9a9230e2011-04-26 14:56:14 +00001217 channel_statistics[CompositeChannels].mean+=
cristyfd9dcd42010-08-08 18:07:02 +00001218 channel_statistics[BlackChannel].mean;
cristy9a9230e2011-04-26 14:56:14 +00001219 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001220 channel_statistics[BlackChannel].variance-
1221 channel_statistics[BlackChannel].mean*
1222 channel_statistics[BlackChannel].mean;
1223 channels++;
1224 }
cristy4c08aed2011-07-01 19:47:50 +00001225 if (((channel & AlphaChannel) != 0) &&
1226 (image->matte != MagickFalse))
1227 {
1228 channel_statistics[CompositeChannels].mean+=
1229 channel_statistics[AlphaChannel].mean;
1230 channel_statistics[CompositeChannels].standard_deviation+=
1231 channel_statistics[AlphaChannel].variance-
1232 channel_statistics[AlphaChannel].mean*
1233 channel_statistics[AlphaChannel].mean;
1234 channels++;
1235 }
cristy9a9230e2011-04-26 14:56:14 +00001236 channel_statistics[CompositeChannels].mean/=channels;
1237 channel_statistics[CompositeChannels].standard_deviation=
1238 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1239 *mean=channel_statistics[CompositeChannels].mean;
1240 *standard_deviation=channel_statistics[CompositeChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001241 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1242 channel_statistics);
1243 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001244}
1245
1246/*
1247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248% %
1249% %
1250% %
1251% G e t I m a g e C h a n n e l K u r t o s i s %
1252% %
1253% %
1254% %
1255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256%
1257% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
1258% image channels.
1259%
1260% The format of the GetImageChannelKurtosis method is:
1261%
1262% MagickBooleanType GetImageChannelKurtosis(const Image *image,
1263% const ChannelType channel,double *kurtosis,double *skewness,
1264% ExceptionInfo *exception)
1265%
1266% A description of each parameter follows:
1267%
1268% o image: the image.
1269%
1270% o channel: the channel.
1271%
1272% o kurtosis: the kurtosis of the channel.
1273%
1274% o skewness: the skewness of the channel.
1275%
1276% o exception: return any errors or warnings in this structure.
1277%
1278*/
1279
1280MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1281 double *kurtosis,double *skewness,ExceptionInfo *exception)
1282{
1283 MagickBooleanType
1284 status;
1285
cristy9a9230e2011-04-26 14:56:14 +00001286 status=GetImageChannelKurtosis(image,CompositeChannels,kurtosis,skewness,
cristy3ed852e2009-09-05 21:47:34 +00001287 exception);
1288 return(status);
1289}
1290
1291MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
1292 const ChannelType channel,double *kurtosis,double *skewness,
1293 ExceptionInfo *exception)
1294{
1295 double
1296 area,
1297 mean,
1298 standard_deviation,
1299 sum_squares,
1300 sum_cubes,
1301 sum_fourth_power;
1302
cristybb503372010-05-27 20:51:26 +00001303 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001304 y;
1305
1306 assert(image != (Image *) NULL);
1307 assert(image->signature == MagickSignature);
1308 if (image->debug != MagickFalse)
1309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1310 *kurtosis=0.0;
1311 *skewness=0.0;
1312 area=0.0;
1313 mean=0.0;
1314 standard_deviation=0.0;
1315 sum_squares=0.0;
1316 sum_cubes=0.0;
1317 sum_fourth_power=0.0;
cristybb503372010-05-27 20:51:26 +00001318 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001319 {
cristy4c08aed2011-07-01 19:47:50 +00001320 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001321 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001322
cristybb503372010-05-27 20:51:26 +00001323 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001324 x;
1325
1326 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001327 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001328 break;
cristybb503372010-05-27 20:51:26 +00001329 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001330 {
1331 if ((channel & RedChannel) != 0)
1332 {
cristy4c08aed2011-07-01 19:47:50 +00001333 mean+=GetPixelRed(image,p);
1334 sum_squares+=(double) GetPixelRed(image,p)*GetPixelRed(image,p);
1335 sum_cubes+=(double) GetPixelRed(image,p)*GetPixelRed(image,p)*
1336 GetPixelRed(image,p);
1337 sum_fourth_power+=(double) GetPixelRed(image,p)*
1338 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001339 area++;
1340 }
1341 if ((channel & GreenChannel) != 0)
1342 {
cristy4c08aed2011-07-01 19:47:50 +00001343 mean+=GetPixelGreen(image,p);
1344 sum_squares+=(double) GetPixelGreen(image,p)*GetPixelGreen(image,p);
1345 sum_cubes+=(double) GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1346 GetPixelGreen(image,p);
1347 sum_fourth_power+=(double) GetPixelGreen(image,p)*
1348 GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1349 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001350 area++;
1351 }
1352 if ((channel & BlueChannel) != 0)
1353 {
cristy4c08aed2011-07-01 19:47:50 +00001354 mean+=GetPixelBlue(image,p);
1355 sum_squares+=(double) GetPixelBlue(image,p)*GetPixelBlue(image,p);
1356 sum_cubes+=(double) GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1357 GetPixelBlue(image,p);
1358 sum_fourth_power+=(double) GetPixelBlue(image,p)*
1359 GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1360 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001361 area++;
1362 }
cristy4c08aed2011-07-01 19:47:50 +00001363 if (((channel & BlackChannel) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001364 (image->colorspace == CMYKColorspace))
1365 {
cristy4c08aed2011-07-01 19:47:50 +00001366 mean+=GetPixelBlack(image,p);
1367 sum_squares+=(double) GetPixelBlack(image,p)*GetPixelBlack(image,p);
1368 sum_cubes+=(double) GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1369 GetPixelBlack(image,p);
1370 sum_fourth_power+=(double) GetPixelBlack(image,p)*
1371 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1372 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001373 area++;
1374 }
cristy4c08aed2011-07-01 19:47:50 +00001375 if ((channel & AlphaChannel) != 0)
1376 {
1377 mean+=GetPixelAlpha(image,p);
1378 sum_squares+=(double) GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
1379 sum_cubes+=(double) GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1380 GetPixelAlpha(image,p);
1381 sum_fourth_power+=(double) GetPixelAlpha(image,p)*
1382 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1383 GetPixelAlpha(image,p);
1384 area++;
1385 }
1386 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001387 }
1388 }
cristybb503372010-05-27 20:51:26 +00001389 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001390 return(MagickFalse);
1391 if (area != 0.0)
1392 {
1393 mean/=area;
1394 sum_squares/=area;
1395 sum_cubes/=area;
1396 sum_fourth_power/=area;
1397 }
1398 standard_deviation=sqrt(sum_squares-(mean*mean));
1399 if (standard_deviation != 0.0)
1400 {
1401 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1402 3.0*mean*mean*mean*mean;
1403 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1404 standard_deviation;
1405 *kurtosis-=3.0;
1406 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1407 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1408 }
cristybb503372010-05-27 20:51:26 +00001409 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001410}
cristy46f08202010-01-10 04:04:21 +00001411
cristy3ed852e2009-09-05 21:47:34 +00001412/*
1413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1414% %
1415% %
1416% %
1417% G e t I m a g e C h a n n e l R a n g e %
1418% %
1419% %
1420% %
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422%
1423% GetImageChannelRange() returns the range of one or more image channels.
1424%
1425% The format of the GetImageChannelRange method is:
1426%
1427% MagickBooleanType GetImageChannelRange(const Image *image,
1428% const ChannelType channel,double *minima,double *maxima,
1429% ExceptionInfo *exception)
1430%
1431% A description of each parameter follows:
1432%
1433% o image: the image.
1434%
1435% o channel: the channel.
1436%
1437% o minima: the minimum value in the channel.
1438%
1439% o maxima: the maximum value in the channel.
1440%
1441% o exception: return any errors or warnings in this structure.
1442%
1443*/
1444
1445MagickExport MagickBooleanType GetImageRange(const Image *image,
1446 double *minima,double *maxima,ExceptionInfo *exception)
1447{
cristy9a9230e2011-04-26 14:56:14 +00001448 return(GetImageChannelRange(image,CompositeChannels,minima,maxima,exception));
cristy3ed852e2009-09-05 21:47:34 +00001449}
1450
1451MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
1452 const ChannelType channel,double *minima,double *maxima,
1453 ExceptionInfo *exception)
1454{
cristy4c08aed2011-07-01 19:47:50 +00001455 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001456 pixel;
1457
cristy9d314ff2011-03-09 01:30:28 +00001458 ssize_t
1459 y;
1460
cristy3ed852e2009-09-05 21:47:34 +00001461 assert(image != (Image *) NULL);
1462 assert(image->signature == MagickSignature);
1463 if (image->debug != MagickFalse)
1464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1465 *maxima=(-1.0E-37);
1466 *minima=1.0E+37;
cristy4c08aed2011-07-01 19:47:50 +00001467 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001469 {
cristy4c08aed2011-07-01 19:47:50 +00001470 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001471 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001472
cristybb503372010-05-27 20:51:26 +00001473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001474 x;
1475
1476 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001477 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001478 break;
cristybb503372010-05-27 20:51:26 +00001479 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001480 {
cristy4c08aed2011-07-01 19:47:50 +00001481 SetPixelInfo(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00001482 if ((channel & RedChannel) != 0)
1483 {
1484 if (pixel.red < *minima)
1485 *minima=(double) pixel.red;
1486 if (pixel.red > *maxima)
1487 *maxima=(double) pixel.red;
1488 }
1489 if ((channel & GreenChannel) != 0)
1490 {
1491 if (pixel.green < *minima)
1492 *minima=(double) pixel.green;
1493 if (pixel.green > *maxima)
1494 *maxima=(double) pixel.green;
1495 }
1496 if ((channel & BlueChannel) != 0)
1497 {
1498 if (pixel.blue < *minima)
1499 *minima=(double) pixel.blue;
1500 if (pixel.blue > *maxima)
1501 *maxima=(double) pixel.blue;
1502 }
cristy4c08aed2011-07-01 19:47:50 +00001503 if (((channel & BlackChannel) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001504 (image->colorspace == CMYKColorspace))
1505 {
cristy4c08aed2011-07-01 19:47:50 +00001506 if (pixel.black < *minima)
1507 *minima=(double) pixel.black;
1508 if (pixel.black > *maxima)
1509 *maxima=(double) pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00001510 }
cristy4c08aed2011-07-01 19:47:50 +00001511 if ((channel & AlphaChannel) != 0)
1512 {
1513 if (pixel.alpha < *minima)
1514 *minima=(double) pixel.alpha;
1515 if (pixel.alpha > *maxima)
1516 *maxima=(double) pixel.alpha;
1517 }
1518 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001519 }
1520 }
cristybb503372010-05-27 20:51:26 +00001521 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001522}
1523
1524/*
1525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1526% %
1527% %
1528% %
1529% 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 %
1530% %
1531% %
1532% %
1533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1534%
1535% GetImageChannelStatistics() returns statistics for each channel in the
1536% image. The statistics include the channel depth, its minima, maxima, mean,
1537% standard deviation, kurtosis and skewness. You can access the red channel
1538% mean, for example, like this:
1539%
cristy0c1c3fd2011-01-18 15:53:10 +00001540% channel_statistics=GetImageChannelStatistics(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001541% red_mean=channel_statistics[RedChannel].mean;
1542%
1543% Use MagickRelinquishMemory() to free the statistics buffer.
1544%
1545% The format of the GetImageChannelStatistics method is:
1546%
1547% ChannelStatistics *GetImageChannelStatistics(const Image *image,
1548% ExceptionInfo *exception)
1549%
1550% A description of each parameter follows:
1551%
1552% o image: the image.
1553%
1554% o exception: return any errors or warnings in this structure.
1555%
1556*/
cristy3ed852e2009-09-05 21:47:34 +00001557MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
1558 ExceptionInfo *exception)
1559{
1560 ChannelStatistics
1561 *channel_statistics;
1562
1563 double
cristyfd9dcd42010-08-08 18:07:02 +00001564 area;
cristy3ed852e2009-09-05 21:47:34 +00001565
cristy3ed852e2009-09-05 21:47:34 +00001566 MagickStatusType
1567 status;
1568
1569 QuantumAny
1570 range;
1571
cristybb503372010-05-27 20:51:26 +00001572 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001573 i;
1574
1575 size_t
cristy9d314ff2011-03-09 01:30:28 +00001576 channels,
1577 depth,
cristy3ed852e2009-09-05 21:47:34 +00001578 length;
1579
cristy9d314ff2011-03-09 01:30:28 +00001580 ssize_t
1581 y;
cristy3ed852e2009-09-05 21:47:34 +00001582
1583 assert(image != (Image *) NULL);
1584 assert(image->signature == MagickSignature);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy9a9230e2011-04-26 14:56:14 +00001587 length=CompositeChannels+1UL;
cristy3ed852e2009-09-05 21:47:34 +00001588 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
1589 sizeof(*channel_statistics));
1590 if (channel_statistics == (ChannelStatistics *) NULL)
1591 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1592 (void) ResetMagickMemory(channel_statistics,0,length*
1593 sizeof(*channel_statistics));
cristy32daba42011-05-01 02:34:39 +00001594 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001595 {
1596 channel_statistics[i].depth=1;
1597 channel_statistics[i].maxima=(-1.0E-37);
1598 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001599 }
cristybb503372010-05-27 20:51:26 +00001600 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001601 {
cristy4c08aed2011-07-01 19:47:50 +00001602 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001603 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001604
cristybb503372010-05-27 20:51:26 +00001605 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001606 x;
1607
1608 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001609 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001610 break;
cristybb503372010-05-27 20:51:26 +00001611 for (x=0; x < (ssize_t) image->columns; )
cristy3ed852e2009-09-05 21:47:34 +00001612 {
1613 if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1614 {
1615 depth=channel_statistics[RedChannel].depth;
1616 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001617 status=GetPixelRed(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1618 GetPixelRed(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001619 if (status != MagickFalse)
1620 {
1621 channel_statistics[RedChannel].depth++;
1622 continue;
1623 }
1624 }
1625 if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1626 {
1627 depth=channel_statistics[GreenChannel].depth;
1628 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001629 status=GetPixelGreen(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1630 GetPixelGreen(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001631 if (status != MagickFalse)
1632 {
1633 channel_statistics[GreenChannel].depth++;
1634 continue;
1635 }
1636 }
1637 if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1638 {
1639 depth=channel_statistics[BlueChannel].depth;
1640 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001641 status=GetPixelBlue(image,p) != ScaleAnyToQuantum(ScaleQuantumToAny(
1642 GetPixelBlue(image,p),range),range) ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001643 if (status != MagickFalse)
1644 {
1645 channel_statistics[BlueChannel].depth++;
1646 continue;
1647 }
1648 }
1649 if (image->matte != MagickFalse)
1650 {
cristy4c08aed2011-07-01 19:47:50 +00001651 if (channel_statistics[AlphaChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
cristy3ed852e2009-09-05 21:47:34 +00001652 {
cristy4c08aed2011-07-01 19:47:50 +00001653 depth=channel_statistics[AlphaChannel].depth;
cristy3ed852e2009-09-05 21:47:34 +00001654 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001655 status=GetPixelAlpha(image,p) != ScaleAnyToQuantum(
1656 ScaleQuantumToAny(GetPixelAlpha(image,p),range),range) ?
cristy32daba42011-05-01 02:34:39 +00001657 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001658 if (status != MagickFalse)
1659 {
cristy4c08aed2011-07-01 19:47:50 +00001660 channel_statistics[AlphaChannel].depth++;
cristy3ed852e2009-09-05 21:47:34 +00001661 continue;
1662 }
1663 }
1664 }
1665 if (image->colorspace == CMYKColorspace)
1666 {
1667 if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
1668 {
1669 depth=channel_statistics[BlackChannel].depth;
1670 range=GetQuantumRange(depth);
cristy4c08aed2011-07-01 19:47:50 +00001671 status=GetPixelBlack(image,p) != ScaleAnyToQuantum(
1672 ScaleQuantumToAny(GetPixelBlack(image,p),range),range) ?
1673 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001674 if (status != MagickFalse)
1675 {
1676 channel_statistics[BlackChannel].depth++;
1677 continue;
1678 }
1679 }
1680 }
cristy4c08aed2011-07-01 19:47:50 +00001681 if ((double) GetPixelRed(image,p) < channel_statistics[RedChannel].minima)
1682 channel_statistics[RedChannel].minima=(double) GetPixelRed(image,p);
1683 if ((double) GetPixelRed(image,p) > channel_statistics[RedChannel].maxima)
1684 channel_statistics[RedChannel].maxima=(double) GetPixelRed(image,p);
1685 channel_statistics[RedChannel].sum+=GetPixelRed(image,p);
1686 channel_statistics[RedChannel].sum_squared+=(double)
1687 GetPixelRed(image,p)*GetPixelRed(image,p);
cristy32daba42011-05-01 02:34:39 +00001688 channel_statistics[RedChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001689 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p);
cristy32daba42011-05-01 02:34:39 +00001690 channel_statistics[RedChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001691 GetPixelRed(image,p)*GetPixelRed(image,p)*GetPixelRed(image,p)*
1692 GetPixelRed(image,p);
1693 if ((double) GetPixelGreen(image,p) < channel_statistics[GreenChannel].minima)
cristy46f08202010-01-10 04:04:21 +00001694 channel_statistics[GreenChannel].minima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001695 GetPixelGreen(image,p);
1696 if ((double) GetPixelGreen(image,p) > channel_statistics[GreenChannel].maxima)
1697 channel_statistics[GreenChannel].maxima=(double) GetPixelGreen(image,p);
1698 channel_statistics[GreenChannel].sum+=GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001699 channel_statistics[GreenChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001700 GetPixelGreen(image,p)*GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001701 channel_statistics[GreenChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001702 GetPixelGreen(image,p)*GetPixelGreen(image,p)*GetPixelGreen(image,p);
cristy32daba42011-05-01 02:34:39 +00001703 channel_statistics[GreenChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001704 GetPixelGreen(image,p)*GetPixelGreen(image,p)*GetPixelGreen(image,p)*
1705 GetPixelGreen(image,p);
1706 if ((double) GetPixelBlue(image,p) < channel_statistics[BlueChannel].minima)
1707 channel_statistics[BlueChannel].minima=(double) GetPixelBlue(image,p);
1708 if ((double) GetPixelBlue(image,p) > channel_statistics[BlueChannel].maxima)
1709 channel_statistics[BlueChannel].maxima=(double) GetPixelBlue(image,p);
1710 channel_statistics[BlueChannel].sum+=GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001711 channel_statistics[BlueChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001712 GetPixelBlue(image,p)*GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001713 channel_statistics[BlueChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001714 GetPixelBlue(image,p)*GetPixelBlue(image,p)*GetPixelBlue(image,p);
cristy32daba42011-05-01 02:34:39 +00001715 channel_statistics[BlueChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001716 GetPixelBlue(image,p)*GetPixelBlue(image,p)*GetPixelBlue(image,p)*
1717 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001718 if (image->colorspace == CMYKColorspace)
1719 {
cristy4c08aed2011-07-01 19:47:50 +00001720 if ((double) GetPixelBlack(image,p) < channel_statistics[BlackChannel].minima)
cristyfba5a8b2011-05-03 17:12:12 +00001721 channel_statistics[BlackChannel].minima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001722 GetPixelBlack(image,p);
1723 if ((double) GetPixelBlack(image,p) > channel_statistics[BlackChannel].maxima)
cristyfba5a8b2011-05-03 17:12:12 +00001724 channel_statistics[BlackChannel].maxima=(double)
cristy4c08aed2011-07-01 19:47:50 +00001725 GetPixelBlack(image,p);
1726 channel_statistics[BlackChannel].sum+=GetPixelBlack(image,p);
cristyfd9dcd42010-08-08 18:07:02 +00001727 channel_statistics[BlackChannel].sum_squared+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001728 GetPixelBlack(image,p)*GetPixelBlack(image,p);
cristyfba5a8b2011-05-03 17:12:12 +00001729 channel_statistics[BlackChannel].sum_cubed+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001730 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1731 GetPixelBlack(image,p);
cristya8178ed2010-08-10 17:31:59 +00001732 channel_statistics[BlackChannel].sum_fourth_power+=(double)
cristy4c08aed2011-07-01 19:47:50 +00001733 GetPixelBlack(image,p)*GetPixelBlack(image,p)*
1734 GetPixelBlack(image,p)*GetPixelBlack(image,p);
1735 }
1736 if (image->matte != MagickFalse)
1737 {
1738 if ((double) GetPixelAlpha(image,p) < channel_statistics[AlphaChannel].minima)
1739 channel_statistics[AlphaChannel].minima=(double)
1740 GetPixelAlpha(image,p);
1741 if ((double) GetPixelAlpha(image,p) > channel_statistics[AlphaChannel].maxima)
1742 channel_statistics[AlphaChannel].maxima=(double)
1743 GetPixelAlpha(image,p);
1744 channel_statistics[AlphaChannel].sum+=GetPixelAlpha(image,p);
1745 channel_statistics[AlphaChannel].sum_squared+=(double)
1746 GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
1747 channel_statistics[AlphaChannel].sum_cubed+=(double)
1748 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1749 GetPixelAlpha(image,p);
1750 channel_statistics[AlphaChannel].sum_fourth_power+=(double)
1751 GetPixelAlpha(image,p)*GetPixelAlpha(image,p)*
1752 GetPixelAlpha(image,p)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001753 }
1754 x++;
cristy4c08aed2011-07-01 19:47:50 +00001755 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001756 }
1757 }
1758 area=(double) image->columns*image->rows;
cristy32daba42011-05-01 02:34:39 +00001759 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001760 {
cristyfd9dcd42010-08-08 18:07:02 +00001761 channel_statistics[i].sum/=area;
1762 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001763 channel_statistics[i].sum_cubed/=area;
1764 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001765 channel_statistics[i].mean=channel_statistics[i].sum;
1766 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1767 channel_statistics[i].standard_deviation=sqrt(
1768 channel_statistics[i].variance-(channel_statistics[i].mean*
1769 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001770 }
cristy32daba42011-05-01 02:34:39 +00001771 for (i=0; i < (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001772 {
cristy9a9230e2011-04-26 14:56:14 +00001773 channel_statistics[CompositeChannels].depth=(size_t) MagickMax((double)
1774 channel_statistics[CompositeChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001775 channel_statistics[i].depth);
cristy9a9230e2011-04-26 14:56:14 +00001776 channel_statistics[CompositeChannels].minima=MagickMin(
cristy32daba42011-05-01 02:34:39 +00001777 channel_statistics[CompositeChannels].minima,
1778 channel_statistics[i].minima);
cristy9a9230e2011-04-26 14:56:14 +00001779 channel_statistics[CompositeChannels].maxima=MagickMax(
cristy32daba42011-05-01 02:34:39 +00001780 channel_statistics[CompositeChannels].maxima,
1781 channel_statistics[i].maxima);
cristy9a9230e2011-04-26 14:56:14 +00001782 channel_statistics[CompositeChannels].sum+=channel_statistics[i].sum;
1783 channel_statistics[CompositeChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001784 channel_statistics[i].sum_squared;
cristy32daba42011-05-01 02:34:39 +00001785 channel_statistics[CompositeChannels].sum_cubed+=
1786 channel_statistics[i].sum_cubed;
cristy9a9230e2011-04-26 14:56:14 +00001787 channel_statistics[CompositeChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001788 channel_statistics[i].sum_fourth_power;
cristy9a9230e2011-04-26 14:56:14 +00001789 channel_statistics[CompositeChannels].mean+=channel_statistics[i].mean;
cristy32daba42011-05-01 02:34:39 +00001790 channel_statistics[CompositeChannels].variance+=
1791 channel_statistics[i].variance-channel_statistics[i].mean*
1792 channel_statistics[i].mean;
cristy9a9230e2011-04-26 14:56:14 +00001793 channel_statistics[CompositeChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001794 channel_statistics[i].variance-channel_statistics[i].mean*
1795 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001796 }
cristycf584452010-08-08 02:26:04 +00001797 channels=3;
1798 if (image->matte != MagickFalse)
1799 channels++;
cristy3ed852e2009-09-05 21:47:34 +00001800 if (image->colorspace == CMYKColorspace)
1801 channels++;
cristy9a9230e2011-04-26 14:56:14 +00001802 channel_statistics[CompositeChannels].sum/=channels;
1803 channel_statistics[CompositeChannels].sum_squared/=channels;
1804 channel_statistics[CompositeChannels].sum_cubed/=channels;
1805 channel_statistics[CompositeChannels].sum_fourth_power/=channels;
1806 channel_statistics[CompositeChannels].mean/=channels;
1807 channel_statistics[CompositeChannels].variance/=channels;
1808 channel_statistics[CompositeChannels].standard_deviation=
1809 sqrt(channel_statistics[CompositeChannels].standard_deviation/channels);
1810 channel_statistics[CompositeChannels].kurtosis/=channels;
1811 channel_statistics[CompositeChannels].skewness/=channels;
cristy32daba42011-05-01 02:34:39 +00001812 for (i=0; i <= (ssize_t) CompositeChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001813 {
cristy3ed852e2009-09-05 21:47:34 +00001814 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001815 continue;
1816 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1817 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1818 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1819 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1820 channel_statistics[i].standard_deviation*
1821 channel_statistics[i].standard_deviation);
1822 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1823 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1824 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1825 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1826 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1827 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1828 channel_statistics[i].standard_deviation*
1829 channel_statistics[i].standard_deviation*
1830 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001831 }
1832 return(channel_statistics);
1833}