blob: a19bb26b77abde25e3bfbce4410d3d6c39fc6bf2 [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"
cristy8ea81222011-09-04 10:33:32 +000066#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000067#include "MagickCore/geometry.h"
68#include "MagickCore/list.h"
69#include "MagickCore/image-private.h"
70#include "MagickCore/magic.h"
71#include "MagickCore/magick.h"
72#include "MagickCore/memory_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/monitor.h"
75#include "MagickCore/monitor-private.h"
76#include "MagickCore/option.h"
77#include "MagickCore/paint.h"
78#include "MagickCore/pixel-accessor.h"
79#include "MagickCore/profile.h"
80#include "MagickCore/quantize.h"
81#include "MagickCore/quantum-private.h"
82#include "MagickCore/random_.h"
83#include "MagickCore/random-private.h"
84#include "MagickCore/segment.h"
85#include "MagickCore/semaphore.h"
86#include "MagickCore/signature-private.h"
87#include "MagickCore/statistic.h"
88#include "MagickCore/string_.h"
89#include "MagickCore/thread-private.h"
90#include "MagickCore/timer.h"
91#include "MagickCore/utility.h"
92#include "MagickCore/version.h"
cristy3ed852e2009-09-05 21:47:34 +000093
94/*
95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96% %
97% %
98% %
cristyd18ae7c2010-03-07 17:39:52 +000099% E v a l u a t e I m a g e %
cristy316d5172009-09-17 19:31:25 +0000100% %
101% %
102% %
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104%
cristyd18ae7c2010-03-07 17:39:52 +0000105% EvaluateImage() applies a value to the image with an arithmetic, relational,
106% or logical operator to an image. Use these operations to lighten or darken
107% an image, to increase or decrease contrast in an image, or to produce the
108% "negative" of an image.
cristy316d5172009-09-17 19:31:25 +0000109%
cristyd42d9952011-07-08 14:21:50 +0000110% The format of the EvaluateImage method is:
cristy316d5172009-09-17 19:31:25 +0000111%
cristyd18ae7c2010-03-07 17:39:52 +0000112% MagickBooleanType EvaluateImage(Image *image,
113% const MagickEvaluateOperator op,const double value,
114% ExceptionInfo *exception)
115% MagickBooleanType EvaluateImages(Image *images,
116% const MagickEvaluateOperator op,const double value,
117% ExceptionInfo *exception)
cristy316d5172009-09-17 19:31:25 +0000118%
119% A description of each parameter follows:
120%
cristyd18ae7c2010-03-07 17:39:52 +0000121% o image: the image.
122%
cristyd18ae7c2010-03-07 17:39:52 +0000123% o op: A channel op.
124%
125% o value: A value value.
cristy316d5172009-09-17 19:31:25 +0000126%
127% o exception: return any errors or warnings in this structure.
128%
129*/
130
cristy4c08aed2011-07-01 19:47:50 +0000131static PixelInfo **DestroyPixelThreadSet(PixelInfo **pixels)
cristy316d5172009-09-17 19:31:25 +0000132{
cristybb503372010-05-27 20:51:26 +0000133 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000134 i;
135
cristy4c08aed2011-07-01 19:47:50 +0000136 assert(pixels != (PixelInfo **) NULL);
cristybb503372010-05-27 20:51:26 +0000137 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy4c08aed2011-07-01 19:47:50 +0000138 if (pixels[i] != (PixelInfo *) NULL)
139 pixels[i]=(PixelInfo *) RelinquishMagickMemory(pixels[i]);
140 pixels=(PixelInfo **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000141 return(pixels);
142}
143
cristy4c08aed2011-07-01 19:47:50 +0000144static PixelInfo **AcquirePixelThreadSet(const Image *image,
cristy08a3d702010-11-28 01:57:36 +0000145 const size_t number_images)
cristy316d5172009-09-17 19:31:25 +0000146{
cristybb503372010-05-27 20:51:26 +0000147 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000148 i,
149 j;
150
cristy4c08aed2011-07-01 19:47:50 +0000151 PixelInfo
cristy316d5172009-09-17 19:31:25 +0000152 **pixels;
153
cristybb503372010-05-27 20:51:26 +0000154 size_t
cristy08a3d702010-11-28 01:57:36 +0000155 length,
cristy316d5172009-09-17 19:31:25 +0000156 number_threads;
157
158 number_threads=GetOpenMPMaximumThreads();
cristy4c08aed2011-07-01 19:47:50 +0000159 pixels=(PixelInfo **) AcquireQuantumMemory(number_threads,
cristy316d5172009-09-17 19:31:25 +0000160 sizeof(*pixels));
cristy4c08aed2011-07-01 19:47:50 +0000161 if (pixels == (PixelInfo **) NULL)
162 return((PixelInfo **) NULL);
cristy316d5172009-09-17 19:31:25 +0000163 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000164 for (i=0; i < (ssize_t) number_threads; i++)
cristy316d5172009-09-17 19:31:25 +0000165 {
cristy08a3d702010-11-28 01:57:36 +0000166 length=image->columns;
167 if (length < number_images)
168 length=number_images;
cristy49dd6a02011-09-24 23:08:01 +0000169 pixels[i]=(PixelInfo *) AcquireQuantumMemory(length,sizeof(**pixels));
cristy4c08aed2011-07-01 19:47:50 +0000170 if (pixels[i] == (PixelInfo *) NULL)
cristy316d5172009-09-17 19:31:25 +0000171 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000172 for (j=0; j < (ssize_t) length; j++)
cristy4c08aed2011-07-01 19:47:50 +0000173 GetPixelInfo(image,&pixels[i][j]);
cristy316d5172009-09-17 19:31:25 +0000174 }
175 return(pixels);
176}
177
cristy351842f2010-03-07 15:27:38 +0000178static inline double MagickMax(const double x,const double y)
179{
180 if (x > y)
181 return(x);
182 return(y);
183}
184
cristy08a3d702010-11-28 01:57:36 +0000185#if defined(__cplusplus) || defined(c_plusplus)
186extern "C" {
187#endif
188
189static int IntensityCompare(const void *x,const void *y)
190{
cristy4c08aed2011-07-01 19:47:50 +0000191 const PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000192 *color_1,
193 *color_2;
194
195 int
196 intensity;
197
cristy4c08aed2011-07-01 19:47:50 +0000198 color_1=(const PixelInfo *) x;
199 color_2=(const PixelInfo *) y;
200 intensity=(int) GetPixelInfoIntensity(color_2)-(int)
201 GetPixelInfoIntensity(color_1);
cristy08a3d702010-11-28 01:57:36 +0000202 return(intensity);
203}
204
205#if defined(__cplusplus) || defined(c_plusplus)
206}
207#endif
208
cristy351842f2010-03-07 15:27:38 +0000209static inline double MagickMin(const double x,const double y)
210{
211 if (x < y)
212 return(x);
213 return(y);
214}
215
cristyd18ae7c2010-03-07 17:39:52 +0000216static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
217 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000218{
219 MagickRealType
220 result;
221
222 result=0.0;
223 switch (op)
224 {
225 case UndefinedEvaluateOperator:
226 break;
cristy33aaed22010-08-11 18:10:50 +0000227 case AbsEvaluateOperator:
228 {
229 result=(MagickRealType) fabs((double) (pixel+value));
230 break;
231 }
cristy351842f2010-03-07 15:27:38 +0000232 case AddEvaluateOperator:
233 {
234 result=(MagickRealType) (pixel+value);
235 break;
236 }
237 case AddModulusEvaluateOperator:
238 {
239 /*
240 This returns a 'floored modulus' of the addition which is a
cristy49dd6a02011-09-24 23:08:01 +0000241 positive result. It differs from % or fmod() that returns a
242 'truncated modulus' result, where floor() is replaced by trunc() and
243 could return a negative result (which is clipped).
cristy351842f2010-03-07 15:27:38 +0000244 */
245 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000246 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000247 break;
248 }
249 case AndEvaluateOperator:
250 {
cristy9fe8cd72010-10-19 01:24:07 +0000251 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000252 break;
253 }
254 case CosineEvaluateOperator:
255 {
256 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
257 QuantumScale*pixel*value))+0.5));
258 break;
259 }
260 case DivideEvaluateOperator:
261 {
262 result=pixel/(value == 0.0 ? 1.0 : value);
263 break;
264 }
cristy9fe8cd72010-10-19 01:24:07 +0000265 case ExponentialEvaluateOperator:
266 {
267 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
268 pixel)));
269 break;
270 }
cristy351842f2010-03-07 15:27:38 +0000271 case GaussianNoiseEvaluateOperator:
272 {
273 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
274 GaussianNoise,value);
275 break;
276 }
277 case ImpulseNoiseEvaluateOperator:
278 {
279 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
280 ImpulseNoise,value);
281 break;
282 }
283 case LaplacianNoiseEvaluateOperator:
284 {
285 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
286 LaplacianNoise,value);
287 break;
288 }
289 case LeftShiftEvaluateOperator:
290 {
cristy9fe8cd72010-10-19 01:24:07 +0000291 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000292 break;
293 }
294 case LogEvaluateOperator:
295 {
296 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
297 pixel+1.0))/log((double) (value+1.0)));
298 break;
299 }
300 case MaxEvaluateOperator:
301 {
302 result=(MagickRealType) MagickMax((double) pixel,value);
303 break;
304 }
305 case MeanEvaluateOperator:
306 {
cristy125a5a32010-05-07 13:30:52 +0000307 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000308 break;
309 }
cristy08a3d702010-11-28 01:57:36 +0000310 case MedianEvaluateOperator:
311 {
312 result=(MagickRealType) (pixel+value);
313 break;
314 }
cristy351842f2010-03-07 15:27:38 +0000315 case MinEvaluateOperator:
316 {
317 result=(MagickRealType) MagickMin((double) pixel,value);
318 break;
319 }
320 case MultiplicativeNoiseEvaluateOperator:
321 {
322 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
323 MultiplicativeGaussianNoise,value);
324 break;
325 }
326 case MultiplyEvaluateOperator:
327 {
328 result=(MagickRealType) (value*pixel);
329 break;
330 }
331 case OrEvaluateOperator:
332 {
cristy9fe8cd72010-10-19 01:24:07 +0000333 result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000334 break;
335 }
336 case PoissonNoiseEvaluateOperator:
337 {
338 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
339 PoissonNoise,value);
340 break;
341 }
342 case PowEvaluateOperator:
343 {
344 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
345 (double) value));
346 break;
347 }
348 case RightShiftEvaluateOperator:
349 {
cristy9fe8cd72010-10-19 01:24:07 +0000350 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000351 break;
352 }
353 case SetEvaluateOperator:
354 {
355 result=value;
356 break;
357 }
358 case SineEvaluateOperator:
359 {
360 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
361 QuantumScale*pixel*value))+0.5));
362 break;
363 }
364 case SubtractEvaluateOperator:
365 {
366 result=(MagickRealType) (pixel-value);
367 break;
368 }
369 case ThresholdEvaluateOperator:
370 {
371 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
372 QuantumRange);
373 break;
374 }
375 case ThresholdBlackEvaluateOperator:
376 {
377 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
378 break;
379 }
380 case ThresholdWhiteEvaluateOperator:
381 {
382 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
383 pixel);
384 break;
385 }
386 case UniformNoiseEvaluateOperator:
387 {
388 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
389 UniformNoise,value);
390 break;
391 }
392 case XorEvaluateOperator:
393 {
cristy9fe8cd72010-10-19 01:24:07 +0000394 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000395 break;
396 }
397 }
cristyd18ae7c2010-03-07 17:39:52 +0000398 return(result);
cristy351842f2010-03-07 15:27:38 +0000399}
400
cristyd18ae7c2010-03-07 17:39:52 +0000401MagickExport Image *EvaluateImages(const Image *images,
402 const MagickEvaluateOperator op,ExceptionInfo *exception)
403{
404#define EvaluateImageTag "Evaluate/Image"
405
406 CacheView
407 *evaluate_view;
408
409 const Image
410 *next;
411
412 Image
413 *evaluate_image;
414
cristyd18ae7c2010-03-07 17:39:52 +0000415 MagickBooleanType
416 status;
417
cristy5f959472010-05-27 22:19:46 +0000418 MagickOffsetType
419 progress;
420
cristy4c08aed2011-07-01 19:47:50 +0000421 PixelInfo
cristyd18ae7c2010-03-07 17:39:52 +0000422 **restrict evaluate_pixels,
423 zero;
424
425 RandomInfo
426 **restrict random_info;
427
cristybb503372010-05-27 20:51:26 +0000428 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000429 number_images;
430
cristy5f959472010-05-27 22:19:46 +0000431 ssize_t
432 y;
433
cristyd18ae7c2010-03-07 17:39:52 +0000434 /*
435 Ensure the image are the same size.
436 */
437 assert(images != (Image *) NULL);
438 assert(images->signature == MagickSignature);
439 if (images->debug != MagickFalse)
440 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
441 assert(exception != (ExceptionInfo *) NULL);
442 assert(exception->signature == MagickSignature);
443 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
444 if ((next->columns != images->columns) || (next->rows != images->rows))
445 {
446 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
447 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
448 return((Image *) NULL);
449 }
450 /*
451 Initialize evaluate next attributes.
452 */
453 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
454 exception);
455 if (evaluate_image == (Image *) NULL)
456 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000457 if (SetImageStorageClass(evaluate_image,DirectClass,exception) == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000458 {
cristyd18ae7c2010-03-07 17:39:52 +0000459 evaluate_image=DestroyImage(evaluate_image);
460 return((Image *) NULL);
461 }
cristy08a3d702010-11-28 01:57:36 +0000462 number_images=GetImageListLength(images);
463 evaluate_pixels=AcquirePixelThreadSet(images,number_images);
cristy4c08aed2011-07-01 19:47:50 +0000464 if (evaluate_pixels == (PixelInfo **) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000465 {
466 evaluate_image=DestroyImage(evaluate_image);
467 (void) ThrowMagickException(exception,GetMagickModule(),
468 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
469 return((Image *) NULL);
470 }
471 /*
472 Evaluate image pixels.
473 */
474 status=MagickTrue;
475 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000476 GetPixelInfo(images,&zero);
cristyd18ae7c2010-03-07 17:39:52 +0000477 random_info=AcquireRandomInfoThreadSet();
cristyd18ae7c2010-03-07 17:39:52 +0000478 evaluate_view=AcquireCacheView(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000479 if (op == MedianEvaluateOperator)
cristyd18ae7c2010-03-07 17:39:52 +0000480#if defined(MAGICKCORE_OPENMP_SUPPORT)
481 #pragma omp parallel for schedule(dynamic) shared(progress,status)
482#endif
cristy08a3d702010-11-28 01:57:36 +0000483 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000484 {
cristy08a3d702010-11-28 01:57:36 +0000485 CacheView
486 *image_view;
cristyd18ae7c2010-03-07 17:39:52 +0000487
cristy08a3d702010-11-28 01:57:36 +0000488 const Image
489 *next;
cristyd18ae7c2010-03-07 17:39:52 +0000490
cristy08a3d702010-11-28 01:57:36 +0000491 const int
492 id = GetOpenMPThreadId();
493
cristy4c08aed2011-07-01 19:47:50 +0000494 register PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000495 *evaluate_pixel;
496
cristy4c08aed2011-07-01 19:47:50 +0000497 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000498 *restrict q;
499
500 register ssize_t
501 x;
502
503 if (status == MagickFalse)
504 continue;
505 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
506 1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000507 if (q == (Quantum *) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000508 {
cristy08a3d702010-11-28 01:57:36 +0000509 status=MagickFalse;
510 continue;
cristyd18ae7c2010-03-07 17:39:52 +0000511 }
cristy08a3d702010-11-28 01:57:36 +0000512 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000513 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000514 {
cristy08a3d702010-11-28 01:57:36 +0000515 register ssize_t
516 i;
517
518 for (i=0; i < (ssize_t) number_images; i++)
519 evaluate_pixel[i]=zero;
520 next=images;
521 for (i=0; i < (ssize_t) number_images; i++)
522 {
cristy4c08aed2011-07-01 19:47:50 +0000523 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000524 *p;
525
526 image_view=AcquireCacheView(next);
527 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000528 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000529 {
530 image_view=DestroyCacheView(image_view);
531 break;
532 }
cristy08a3d702010-11-28 01:57:36 +0000533 evaluate_pixel[i].red=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000534 GetPixelRed(next,p),op,evaluate_pixel[i].red);
cristy08a3d702010-11-28 01:57:36 +0000535 evaluate_pixel[i].green=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000536 GetPixelGreen(next,p),op,evaluate_pixel[i].green);
cristy08a3d702010-11-28 01:57:36 +0000537 evaluate_pixel[i].blue=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000538 GetPixelBlue(next,p),op,evaluate_pixel[i].blue);
cristy08a3d702010-11-28 01:57:36 +0000539 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000540 evaluate_pixel[i].black=ApplyEvaluateOperator(random_info[id],
541 GetPixelBlack(next,p),op,evaluate_pixel[i].black);
542 evaluate_pixel[i].alpha=ApplyEvaluateOperator(random_info[id],
543 GetPixelAlpha(next,p),op,evaluate_pixel[i].alpha);
cristy08a3d702010-11-28 01:57:36 +0000544 image_view=DestroyCacheView(image_view);
545 next=GetNextImageInList(next);
546 }
547 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
548 IntensityCompare);
cristy4c08aed2011-07-01 19:47:50 +0000549 SetPixelRed(evaluate_image,
550 ClampToQuantum(evaluate_pixel[i/2].red),q);
551 SetPixelGreen(evaluate_image,
552 ClampToQuantum(evaluate_pixel[i/2].green),q);
553 SetPixelBlue(evaluate_image,
554 ClampToQuantum(evaluate_pixel[i/2].blue),q);
cristy08a3d702010-11-28 01:57:36 +0000555 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000556 SetPixelBlack(evaluate_image,
557 ClampToQuantum(evaluate_pixel[i/2].black),q);
558 if (evaluate_image->matte == MagickFalse)
559 SetPixelAlpha(evaluate_image,
560 ClampToQuantum(evaluate_pixel[i/2].alpha),q);
561 else
562 SetPixelAlpha(evaluate_image,
563 ClampToQuantum(evaluate_pixel[i/2].alpha),q);
cristyed231572011-07-14 02:18:59 +0000564 q+=GetPixelChannels(evaluate_image);
cristy125a5a32010-05-07 13:30:52 +0000565 }
cristy08a3d702010-11-28 01:57:36 +0000566 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
567 status=MagickFalse;
568 if (images->progress_monitor != (MagickProgressMonitor) NULL)
569 {
570 MagickBooleanType
571 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000572
573#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000574 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000575#endif
cristy08a3d702010-11-28 01:57:36 +0000576 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
577 evaluate_image->rows);
578 if (proceed == MagickFalse)
579 status=MagickFalse;
580 }
581 }
582 else
583#if defined(MAGICKCORE_OPENMP_SUPPORT)
584 #pragma omp parallel for schedule(dynamic) shared(progress,status)
585#endif
586 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
587 {
588 CacheView
589 *image_view;
590
591 const Image
592 *next;
593
594 const int
595 id = GetOpenMPThreadId();
596
cristy08a3d702010-11-28 01:57:36 +0000597 register ssize_t
598 i,
599 x;
600
cristy4c08aed2011-07-01 19:47:50 +0000601 register PixelInfo
cristy08a3d702010-11-28 01:57:36 +0000602 *evaluate_pixel;
603
cristy4c08aed2011-07-01 19:47:50 +0000604 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000605 *restrict q;
606
607 if (status == MagickFalse)
608 continue;
609 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
610 1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000611 if (q == (Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000612 {
cristyd18ae7c2010-03-07 17:39:52 +0000613 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000614 continue;
615 }
cristy08a3d702010-11-28 01:57:36 +0000616 evaluate_pixel=evaluate_pixels[id];
617 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
618 evaluate_pixel[x]=zero;
619 next=images;
620 for (i=0; i < (ssize_t) number_images; i++)
621 {
cristy4c08aed2011-07-01 19:47:50 +0000622 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000623 *p;
624
625 image_view=AcquireCacheView(next);
626 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000627 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000628 {
629 image_view=DestroyCacheView(image_view);
630 break;
631 }
cristy08a3d702010-11-28 01:57:36 +0000632 for (x=0; x < (ssize_t) next->columns; x++)
633 {
634 evaluate_pixel[x].red=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000635 GetPixelRed(next,p),i == 0 ? AddEvaluateOperator : op,
636 evaluate_pixel[x].red);
cristy08a3d702010-11-28 01:57:36 +0000637 evaluate_pixel[x].green=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000638 GetPixelGreen(next,p),i == 0 ? AddEvaluateOperator : op,
639 evaluate_pixel[x].green);
cristy08a3d702010-11-28 01:57:36 +0000640 evaluate_pixel[x].blue=ApplyEvaluateOperator(random_info[id],
cristy4c08aed2011-07-01 19:47:50 +0000641 GetPixelBlue(next,p),i == 0 ? AddEvaluateOperator : op,
642 evaluate_pixel[x].blue);
cristy08a3d702010-11-28 01:57:36 +0000643 if (evaluate_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000644 evaluate_pixel[x].black=ApplyEvaluateOperator(random_info[id],
645 GetPixelBlack(next,p),i == 0 ? AddEvaluateOperator : op,
646 evaluate_pixel[x].black);
647 evaluate_pixel[x].alpha=ApplyEvaluateOperator(random_info[id],
648 GetPixelAlpha(next,p),i == 0 ? AddEvaluateOperator : op,
649 evaluate_pixel[x].alpha);
cristyed231572011-07-14 02:18:59 +0000650 p+=GetPixelChannels(next);
cristy08a3d702010-11-28 01:57:36 +0000651 }
652 image_view=DestroyCacheView(image_view);
653 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000654 }
cristy08a3d702010-11-28 01:57:36 +0000655 if (op == MeanEvaluateOperator)
656 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
657 {
658 evaluate_pixel[x].red/=number_images;
659 evaluate_pixel[x].green/=number_images;
660 evaluate_pixel[x].blue/=number_images;
cristy4c08aed2011-07-01 19:47:50 +0000661 evaluate_pixel[x].black/=number_images;
662 evaluate_pixel[x].alpha/=number_images;
cristy08a3d702010-11-28 01:57:36 +0000663 }
664 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
665 {
cristy4c08aed2011-07-01 19:47:50 +0000666 SetPixelRed(evaluate_image,ClampToQuantum(evaluate_pixel[x].red),q);
667 SetPixelGreen(evaluate_image,ClampToQuantum(evaluate_pixel[x].green),q);
668 SetPixelBlue(evaluate_image,ClampToQuantum(evaluate_pixel[x].blue),q);
cristy08a3d702010-11-28 01:57:36 +0000669 if (evaluate_image->colorspace == CMYKColorspace)
cristy6fbfa592011-07-03 23:04:58 +0000670 SetPixelBlack(evaluate_image,ClampToQuantum(evaluate_pixel[x].black),
671 q);
cristy4c08aed2011-07-01 19:47:50 +0000672 if (evaluate_image->matte == MagickFalse)
cristy6fbfa592011-07-03 23:04:58 +0000673 SetPixelAlpha(evaluate_image,ClampToQuantum(evaluate_pixel[x].alpha),
674 q);
cristy4c08aed2011-07-01 19:47:50 +0000675 else
cristy6fbfa592011-07-03 23:04:58 +0000676 SetPixelAlpha(evaluate_image,ClampToQuantum(evaluate_pixel[x].alpha),
677 q);
cristyed231572011-07-14 02:18:59 +0000678 q+=GetPixelChannels(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000679 }
680 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
681 status=MagickFalse;
682 if (images->progress_monitor != (MagickProgressMonitor) NULL)
683 {
684 MagickBooleanType
685 proceed;
686
687#if defined(MAGICKCORE_OPENMP_SUPPORT)
688 #pragma omp critical (MagickCore_EvaluateImages)
689#endif
690 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
691 evaluate_image->rows);
692 if (proceed == MagickFalse)
693 status=MagickFalse;
694 }
695 }
cristyd18ae7c2010-03-07 17:39:52 +0000696 evaluate_view=DestroyCacheView(evaluate_view);
697 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
698 random_info=DestroyRandomInfoThreadSet(random_info);
699 if (status == MagickFalse)
700 evaluate_image=DestroyImage(evaluate_image);
701 return(evaluate_image);
702}
703
cristyd42d9952011-07-08 14:21:50 +0000704MagickExport MagickBooleanType EvaluateImage(Image *image,
705 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000706{
cristy351842f2010-03-07 15:27:38 +0000707 CacheView
708 *image_view;
709
cristy351842f2010-03-07 15:27:38 +0000710 MagickBooleanType
711 status;
712
cristy5f959472010-05-27 22:19:46 +0000713 MagickOffsetType
714 progress;
715
cristy351842f2010-03-07 15:27:38 +0000716 RandomInfo
717 **restrict random_info;
718
cristy5f959472010-05-27 22:19:46 +0000719 ssize_t
720 y;
721
cristy351842f2010-03-07 15:27:38 +0000722 assert(image != (Image *) NULL);
723 assert(image->signature == MagickSignature);
724 if (image->debug != MagickFalse)
725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
726 assert(exception != (ExceptionInfo *) NULL);
727 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000728 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
729 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +0000730 status=MagickTrue;
731 progress=0;
732 random_info=AcquireRandomInfoThreadSet();
733 image_view=AcquireCacheView(image);
734#if defined(MAGICKCORE_OPENMP_SUPPORT)
735 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
736#endif
cristybb503372010-05-27 20:51:26 +0000737 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000738 {
cristy5c9e6f22010-09-17 17:31:01 +0000739 const int
740 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000741
cristy4c08aed2011-07-01 19:47:50 +0000742 register Quantum
cristy351842f2010-03-07 15:27:38 +0000743 *restrict q;
744
cristy5c9e6f22010-09-17 17:31:01 +0000745 register ssize_t
746 x;
747
cristy351842f2010-03-07 15:27:38 +0000748 if (status == MagickFalse)
749 continue;
750 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000751 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +0000752 {
753 status=MagickFalse;
754 continue;
755 }
cristybb503372010-05-27 20:51:26 +0000756 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000757 {
cristy49dd6a02011-09-24 23:08:01 +0000758 register ssize_t
759 i;
760
761 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
762 {
763 PixelTrait
764 traits;
765
766 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
767 if (traits == UndefinedPixelTrait)
768 continue;
769 q[i]=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q[i],op,
770 value));
771 }
cristyed231572011-07-14 02:18:59 +0000772 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000773 }
774 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
775 status=MagickFalse;
776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
777 {
778 MagickBooleanType
779 proceed;
780
781#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +0000782 #pragma omp critical (MagickCore_EvaluateImage)
cristy351842f2010-03-07 15:27:38 +0000783#endif
784 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
785 if (proceed == MagickFalse)
786 status=MagickFalse;
787 }
788 }
789 image_view=DestroyCacheView(image_view);
790 random_info=DestroyRandomInfoThreadSet(random_info);
791 return(status);
792}
793
794/*
795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796% %
797% %
798% %
799% F u n c t i o n I m a g e %
800% %
801% %
802% %
803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804%
805% FunctionImage() applies a value to the image with an arithmetic, relational,
806% or logical operator to an image. Use these operations to lighten or darken
807% an image, to increase or decrease contrast in an image, or to produce the
808% "negative" of an image.
809%
cristyd42d9952011-07-08 14:21:50 +0000810% The format of the FunctionImage method is:
cristy351842f2010-03-07 15:27:38 +0000811%
812% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000813% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000814% const double *parameters,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000815%
816% A description of each parameter follows:
817%
818% o image: the image.
819%
cristy351842f2010-03-07 15:27:38 +0000820% o function: A channel function.
821%
822% o parameters: one or more parameters.
823%
824% o exception: return any errors or warnings in this structure.
825%
826*/
827
828static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000829 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000830 ExceptionInfo *exception)
831{
832 MagickRealType
833 result;
834
cristybb503372010-05-27 20:51:26 +0000835 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000836 i;
837
838 (void) exception;
839 result=0.0;
840 switch (function)
841 {
842 case PolynomialFunction:
843 {
844 /*
cristy94a75782011-09-25 02:33:56 +0000845 Polynomial: polynomial constants, highest to lowest order
846 (e.g. c0*x^3 + c1*x^2 + c2*x + c3).
847 */
cristy351842f2010-03-07 15:27:38 +0000848 result=0.0;
cristybb503372010-05-27 20:51:26 +0000849 for (i=0; i < (ssize_t) number_parameters; i++)
cristy94a75782011-09-25 02:33:56 +0000850 result=result*QuantumScale*pixel+parameters[i];
851 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000852 break;
853 }
854 case SinusoidFunction:
855 {
cristy94a75782011-09-25 02:33:56 +0000856 MagickRealType
857 amplitude,
858 bias,
859 frequency,
860 phase;
861
862 /*
863 Sinusoid: frequency, phase, amplitude, bias.
864 */
865 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
866 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
867 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
868 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
869 result=(MagickRealType) (QuantumRange*(amplitude*sin((double) (2.0*
870 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
cristy351842f2010-03-07 15:27:38 +0000871 break;
872 }
873 case ArcsinFunction:
874 {
cristy94a75782011-09-25 02:33:56 +0000875 MagickRealType
876 bias,
877 center,
878 range,
879 width;
880
881 /*
882 Arcsin (peged at range limits for invalid results):
883 width, center, range, and bias.
884 */
885 width=(number_parameters >= 1) ? parameters[0] : 1.0;
886 center=(number_parameters >= 2) ? parameters[1] : 0.5;
887 range=(number_parameters >= 3) ? parameters[2] : 1.0;
888 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
889 result=2.0/width*(QuantumScale*pixel-center);
cristy351842f2010-03-07 15:27:38 +0000890 if ( result <= -1.0 )
cristy94a75782011-09-25 02:33:56 +0000891 result=bias-range/2.0;
cristy351842f2010-03-07 15:27:38 +0000892 else
cristy94a75782011-09-25 02:33:56 +0000893 if (result >= 1.0)
894 result=bias+range/2.0;
895 else
896 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
897 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000898 break;
899 }
900 case ArctanFunction:
901 {
cristy94a75782011-09-25 02:33:56 +0000902 MagickRealType
903 center,
904 bias,
905 range,
906 slope;
907
908 /*
909 Arctan: slope, center, range, and bias.
910 */
911 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
912 center=(number_parameters >= 2) ? parameters[1] : 0.5;
913 range=(number_parameters >= 3) ? parameters[2] : 1.0;
914 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000915 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000916 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
cristy94a75782011-09-25 02:33:56 +0000917 result)+bias));
cristy351842f2010-03-07 15:27:38 +0000918 break;
919 }
920 case UndefinedFunction:
921 break;
922 }
923 return(ClampToQuantum(result));
924}
925
926MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000927 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000928 const double *parameters,ExceptionInfo *exception)
929{
cristy351842f2010-03-07 15:27:38 +0000930#define FunctionImageTag "Function/Image "
931
932 CacheView
933 *image_view;
934
cristy351842f2010-03-07 15:27:38 +0000935 MagickBooleanType
936 status;
937
cristy5f959472010-05-27 22:19:46 +0000938 MagickOffsetType
939 progress;
940
941 ssize_t
942 y;
943
cristy351842f2010-03-07 15:27:38 +0000944 assert(image != (Image *) NULL);
945 assert(image->signature == MagickSignature);
946 if (image->debug != MagickFalse)
947 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
948 assert(exception != (ExceptionInfo *) NULL);
949 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000950 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
951 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +0000952 status=MagickTrue;
953 progress=0;
954 image_view=AcquireCacheView(image);
955#if defined(MAGICKCORE_OPENMP_SUPPORT)
956 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
957#endif
cristybb503372010-05-27 20:51:26 +0000958 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000959 {
cristy4c08aed2011-07-01 19:47:50 +0000960 register Quantum
cristy351842f2010-03-07 15:27:38 +0000961 *restrict q;
962
cristy94a75782011-09-25 02:33:56 +0000963 register ssize_t
964 x;
965
cristy351842f2010-03-07 15:27:38 +0000966 if (status == MagickFalse)
967 continue;
968 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000969 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +0000970 {
971 status=MagickFalse;
972 continue;
973 }
cristybb503372010-05-27 20:51:26 +0000974 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000975 {
cristy94a75782011-09-25 02:33:56 +0000976 register ssize_t
977 i;
978
979 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
980 {
981 PixelTrait
982 traits;
983
984 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
985 if (traits == UndefinedPixelTrait)
986 continue;
987 if ((traits & UpdatePixelTrait) == 0)
988 continue;
989 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
990 exception);
991 }
cristyed231572011-07-14 02:18:59 +0000992 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000993 }
994 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
995 status=MagickFalse;
996 if (image->progress_monitor != (MagickProgressMonitor) NULL)
997 {
998 MagickBooleanType
999 proceed;
1000
1001#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +00001002 #pragma omp critical (MagickCore_FunctionImage)
cristy351842f2010-03-07 15:27:38 +00001003#endif
1004 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1005 if (proceed == MagickFalse)
1006 status=MagickFalse;
1007 }
1008 }
1009 image_view=DestroyCacheView(image_view);
1010 return(status);
1011}
1012
1013/*
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015% %
1016% %
1017% %
cristyd42d9952011-07-08 14:21:50 +00001018% G e t I m a g e E x t r e m a %
cristy3ed852e2009-09-05 21:47:34 +00001019% %
1020% %
1021% %
1022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1023%
cristyd42d9952011-07-08 14:21:50 +00001024% GetImageExtrema() returns the extrema of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001025%
cristyd42d9952011-07-08 14:21:50 +00001026% The format of the GetImageExtrema method is:
cristy3ed852e2009-09-05 21:47:34 +00001027%
cristyd42d9952011-07-08 14:21:50 +00001028% MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1029% size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001030%
1031% A description of each parameter follows:
1032%
1033% o image: the image.
1034%
cristy3ed852e2009-09-05 21:47:34 +00001035% o minima: the minimum value in the channel.
1036%
1037% o maxima: the maximum value in the channel.
1038%
1039% o exception: return any errors or warnings in this structure.
1040%
1041*/
cristy3ed852e2009-09-05 21:47:34 +00001042MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001043 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001044{
cristy3ed852e2009-09-05 21:47:34 +00001045 double
1046 max,
1047 min;
1048
1049 MagickBooleanType
1050 status;
1051
1052 assert(image != (Image *) NULL);
1053 assert(image->signature == MagickSignature);
1054 if (image->debug != MagickFalse)
1055 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001056 status=GetImageRange(image,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001057 *minima=(size_t) ceil(min-0.5);
1058 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001059 return(status);
1060}
1061
1062/*
1063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1064% %
1065% %
1066% %
cristyd42d9952011-07-08 14:21:50 +00001067% G e t I m a g e M e a n %
cristy3ed852e2009-09-05 21:47:34 +00001068% %
1069% %
1070% %
1071%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1072%
cristyd42d9952011-07-08 14:21:50 +00001073% GetImageMean() returns the mean and standard deviation of one or more
cristy3ed852e2009-09-05 21:47:34 +00001074% image channels.
1075%
cristyd42d9952011-07-08 14:21:50 +00001076% The format of the GetImageMean method is:
cristy3ed852e2009-09-05 21:47:34 +00001077%
cristyd42d9952011-07-08 14:21:50 +00001078% MagickBooleanType GetImageMean(const Image *image,double *mean,
1079% double *standard_deviation,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001080%
1081% A description of each parameter follows:
1082%
1083% o image: the image.
1084%
cristy3ed852e2009-09-05 21:47:34 +00001085% o mean: the average value in the channel.
1086%
1087% o standard_deviation: the standard deviation of the channel.
1088%
1089% o exception: return any errors or warnings in this structure.
1090%
1091*/
cristy3ed852e2009-09-05 21:47:34 +00001092MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1093 double *standard_deviation,ExceptionInfo *exception)
1094{
cristyfd9dcd42010-08-08 18:07:02 +00001095 ChannelStatistics
1096 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001097
cristy94a75782011-09-25 02:33:56 +00001098 register ssize_t
1099 i;
1100
cristyfd9dcd42010-08-08 18:07:02 +00001101 size_t
cristy94a75782011-09-25 02:33:56 +00001102 area;
cristy3ed852e2009-09-05 21:47:34 +00001103
1104 assert(image != (Image *) NULL);
1105 assert(image->signature == MagickSignature);
1106 if (image->debug != MagickFalse)
1107 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001108 channel_statistics=GetImageStatistics(image,exception);
cristyfd9dcd42010-08-08 18:07:02 +00001109 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001110 return(MagickFalse);
cristy94a75782011-09-25 02:33:56 +00001111 area=0;
cristy25a5f2f2011-09-24 14:09:43 +00001112 channel_statistics[MaxPixelChannels].mean=0.0;
1113 channel_statistics[MaxPixelChannels].standard_deviation=0.0;
cristy94a75782011-09-25 02:33:56 +00001114 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1115 {
1116 PixelTrait
1117 traits;
1118
1119 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1120 if (traits == UndefinedPixelTrait)
1121 continue;
1122 if ((traits & UpdatePixelTrait) == 0)
1123 continue;
1124 channel_statistics[MaxPixelChannels].mean+=channel_statistics[i].mean;
1125 channel_statistics[MaxPixelChannels].standard_deviation+=
1126 channel_statistics[i].variance-channel_statistics[i].mean*
1127 channel_statistics[i].mean;
1128 area++;
1129 }
1130 channel_statistics[MaxPixelChannels].mean/=area;
cristy25a5f2f2011-09-24 14:09:43 +00001131 channel_statistics[MaxPixelChannels].standard_deviation=
cristy94a75782011-09-25 02:33:56 +00001132 sqrt(channel_statistics[MaxPixelChannels].standard_deviation/area);
cristy25a5f2f2011-09-24 14:09:43 +00001133 *mean=channel_statistics[MaxPixelChannels].mean;
1134 *standard_deviation=channel_statistics[MaxPixelChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001135 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1136 channel_statistics);
1137 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001138}
1139
1140/*
1141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1142% %
1143% %
1144% %
cristyd42d9952011-07-08 14:21:50 +00001145% G e t I m a g e K u r t o s i s %
cristy3ed852e2009-09-05 21:47:34 +00001146% %
1147% %
1148% %
1149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1150%
cristyd42d9952011-07-08 14:21:50 +00001151% GetImageKurtosis() returns the kurtosis and skewness of one or more
cristy3ed852e2009-09-05 21:47:34 +00001152% image channels.
1153%
cristyd42d9952011-07-08 14:21:50 +00001154% The format of the GetImageKurtosis method is:
cristy3ed852e2009-09-05 21:47:34 +00001155%
cristyd42d9952011-07-08 14:21:50 +00001156% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1157% double *skewness,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001158%
1159% A description of each parameter follows:
1160%
1161% o image: the image.
1162%
cristy3ed852e2009-09-05 21:47:34 +00001163% o kurtosis: the kurtosis of the channel.
1164%
1165% o skewness: the skewness of the channel.
1166%
1167% o exception: return any errors or warnings in this structure.
1168%
1169*/
cristy3ed852e2009-09-05 21:47:34 +00001170MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1171 double *kurtosis,double *skewness,ExceptionInfo *exception)
1172{
cristy94a75782011-09-25 02:33:56 +00001173 CacheView
1174 *image_view;
1175
cristy3ed852e2009-09-05 21:47:34 +00001176 double
1177 area,
1178 mean,
1179 standard_deviation,
1180 sum_squares,
1181 sum_cubes,
1182 sum_fourth_power;
1183
cristy94a75782011-09-25 02:33:56 +00001184 MagickBooleanType
1185 status;
1186
cristybb503372010-05-27 20:51:26 +00001187 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001188 y;
1189
1190 assert(image != (Image *) NULL);
1191 assert(image->signature == MagickSignature);
1192 if (image->debug != MagickFalse)
1193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy94a75782011-09-25 02:33:56 +00001194 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001195 *kurtosis=0.0;
1196 *skewness=0.0;
1197 area=0.0;
1198 mean=0.0;
1199 standard_deviation=0.0;
1200 sum_squares=0.0;
1201 sum_cubes=0.0;
1202 sum_fourth_power=0.0;
cristy94a75782011-09-25 02:33:56 +00001203 image_view=AcquireCacheView(image);
1204#if defined(MAGICKCORE_OPENMP_SUPPORT)
1205 #pragma omp parallel for schedule(dynamic) shared(status) omp_throttle(1)
1206#endif
cristybb503372010-05-27 20:51:26 +00001207 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
cristy4c08aed2011-07-01 19:47:50 +00001209 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001210 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001211
cristybb503372010-05-27 20:51:26 +00001212 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001213 x;
1214
cristy94a75782011-09-25 02:33:56 +00001215 if (status == MagickFalse)
1216 continue;
1217 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001218 if (p == (const Quantum *) NULL)
cristy94a75782011-09-25 02:33:56 +00001219 {
1220 status=MagickFalse;
1221 continue;
1222 }
cristybb503372010-05-27 20:51:26 +00001223 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001224 {
cristy94a75782011-09-25 02:33:56 +00001225 register ssize_t
1226 i;
1227
1228 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1229 {
1230 PixelTrait
1231 traits;
1232
1233 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1234 if (traits == UndefinedPixelTrait)
1235 continue;
1236 if ((traits & UpdatePixelTrait) == 0)
1237 continue;
1238#if defined(MAGICKCORE_OPENMP_SUPPORT)
1239 #pragma omp critical (MagickCore_GetImageKurtosis)
1240#endif
cristy3ed852e2009-09-05 21:47:34 +00001241 {
cristy94a75782011-09-25 02:33:56 +00001242 mean+=p[i];
1243 sum_squares+=(double) p[i]*p[i];
1244 sum_cubes+=(double) p[i]*p[i]*p[i];
1245 sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
cristy3ed852e2009-09-05 21:47:34 +00001246 area++;
1247 }
cristy94a75782011-09-25 02:33:56 +00001248 }
cristyed231572011-07-14 02:18:59 +00001249 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001250 }
1251 }
cristy94a75782011-09-25 02:33:56 +00001252 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001253 if (area != 0.0)
1254 {
1255 mean/=area;
1256 sum_squares/=area;
1257 sum_cubes/=area;
1258 sum_fourth_power/=area;
1259 }
1260 standard_deviation=sqrt(sum_squares-(mean*mean));
1261 if (standard_deviation != 0.0)
1262 {
1263 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1264 3.0*mean*mean*mean*mean;
1265 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1266 standard_deviation;
1267 *kurtosis-=3.0;
1268 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1269 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1270 }
cristy94a75782011-09-25 02:33:56 +00001271 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001272}
cristy46f08202010-01-10 04:04:21 +00001273
cristy3ed852e2009-09-05 21:47:34 +00001274/*
1275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276% %
1277% %
1278% %
cristyd42d9952011-07-08 14:21:50 +00001279% G e t I m a g e R a n g e %
cristy3ed852e2009-09-05 21:47:34 +00001280% %
1281% %
1282% %
1283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284%
cristyd42d9952011-07-08 14:21:50 +00001285% GetImageRange() returns the range of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001286%
cristyd42d9952011-07-08 14:21:50 +00001287% The format of the GetImageRange method is:
cristy3ed852e2009-09-05 21:47:34 +00001288%
cristyd42d9952011-07-08 14:21:50 +00001289% MagickBooleanType GetImageRange(const Image *image,double *minima,
1290% double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001291%
1292% A description of each parameter follows:
1293%
1294% o image: the image.
1295%
cristy3ed852e2009-09-05 21:47:34 +00001296% o minima: the minimum value in the channel.
1297%
1298% o maxima: the maximum value in the channel.
1299%
1300% o exception: return any errors or warnings in this structure.
1301%
1302*/
cristyd42d9952011-07-08 14:21:50 +00001303MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1304 double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001305{
cristy4a9be682011-09-25 02:02:32 +00001306 CacheView
1307 *image_view;
1308
1309 MagickBooleanType
1310 status;
cristy3ed852e2009-09-05 21:47:34 +00001311
cristy9d314ff2011-03-09 01:30:28 +00001312 ssize_t
1313 y;
1314
cristy3ed852e2009-09-05 21:47:34 +00001315 assert(image != (Image *) NULL);
1316 assert(image->signature == MagickSignature);
1317 if (image->debug != MagickFalse)
1318 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy4a9be682011-09-25 02:02:32 +00001319 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001320 *maxima=(-1.0E-37);
1321 *minima=1.0E+37;
cristy4a9be682011-09-25 02:02:32 +00001322 image_view=AcquireCacheView(image);
1323#if defined(MAGICKCORE_OPENMP_SUPPORT)
1324 #pragma omp parallel for schedule(dynamic) shared(status) omp_throttle(1)
1325#endif
cristybb503372010-05-27 20:51:26 +00001326 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001327 {
cristy4c08aed2011-07-01 19:47:50 +00001328 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001329 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001330
cristybb503372010-05-27 20:51:26 +00001331 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001332 x;
1333
cristy4a9be682011-09-25 02:02:32 +00001334 if (status == MagickFalse)
1335 continue;
1336 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001337 if (p == (const Quantum *) NULL)
cristy4a9be682011-09-25 02:02:32 +00001338 {
1339 status=MagickFalse;
1340 continue;
1341 }
cristybb503372010-05-27 20:51:26 +00001342 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001343 {
cristy4a9be682011-09-25 02:02:32 +00001344 register ssize_t
1345 i;
1346
1347 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1348 {
1349 PixelTrait
1350 traits;
1351
1352 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1353 if (traits == UndefinedPixelTrait)
1354 continue;
1355 if ((traits & UpdatePixelTrait) == 0)
1356 continue;
1357#if defined(MAGICKCORE_OPENMP_SUPPORT)
1358 #pragma omp critical (MagickCore_GetImageRange)
1359#endif
cristy3ed852e2009-09-05 21:47:34 +00001360 {
cristy94a75782011-09-25 02:33:56 +00001361 if ((double) p[i] < *minima)
cristy4a9be682011-09-25 02:02:32 +00001362 *minima=(double) p[i];
cristy94a75782011-09-25 02:33:56 +00001363 if ((double) p[i] > *maxima)
cristy4a9be682011-09-25 02:02:32 +00001364 *maxima=(double) p[i];
cristy3ed852e2009-09-05 21:47:34 +00001365 }
cristy4a9be682011-09-25 02:02:32 +00001366 }
cristyed231572011-07-14 02:18:59 +00001367 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001368 }
1369 }
cristy4a9be682011-09-25 02:02:32 +00001370 image_view=DestroyCacheView(image_view);
1371 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001372}
1373
1374/*
1375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376% %
1377% %
1378% %
cristyd42d9952011-07-08 14:21:50 +00001379% G e t I m a g e S t a t i s t i c s %
cristy3ed852e2009-09-05 21:47:34 +00001380% %
1381% %
1382% %
1383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1384%
cristyd42d9952011-07-08 14:21:50 +00001385% GetImageStatistics() returns statistics for each channel in the
cristy3ed852e2009-09-05 21:47:34 +00001386% image. The statistics include the channel depth, its minima, maxima, mean,
1387% standard deviation, kurtosis and skewness. You can access the red channel
1388% mean, for example, like this:
1389%
cristyd42d9952011-07-08 14:21:50 +00001390% channel_statistics=GetImageStatistics(image,exception);
cristy49dd6a02011-09-24 23:08:01 +00001391% red_mean=channel_statistics[RedPixelChannel].mean;
cristy3ed852e2009-09-05 21:47:34 +00001392%
1393% Use MagickRelinquishMemory() to free the statistics buffer.
1394%
cristyd42d9952011-07-08 14:21:50 +00001395% The format of the GetImageStatistics method is:
cristy3ed852e2009-09-05 21:47:34 +00001396%
cristyd42d9952011-07-08 14:21:50 +00001397% ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001398% ExceptionInfo *exception)
1399%
1400% A description of each parameter follows:
1401%
1402% o image: the image.
1403%
1404% o exception: return any errors or warnings in this structure.
1405%
1406*/
cristy49dd6a02011-09-24 23:08:01 +00001407
1408static size_t GetImageChannels(const Image *image)
1409{
1410 register ssize_t
1411 i;
1412
1413 size_t
1414 channels;
1415
1416 channels=0;
1417 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1418 {
1419 PixelTrait
1420 traits;
1421
1422 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1423 if ((traits & UpdatePixelTrait) != 0)
1424 channels++;
1425 }
1426 return(channels);
1427}
1428
cristyd42d9952011-07-08 14:21:50 +00001429MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001430 ExceptionInfo *exception)
1431{
1432 ChannelStatistics
1433 *channel_statistics;
1434
1435 double
cristyfd9dcd42010-08-08 18:07:02 +00001436 area;
cristy3ed852e2009-09-05 21:47:34 +00001437
cristy3ed852e2009-09-05 21:47:34 +00001438 MagickStatusType
1439 status;
1440
1441 QuantumAny
1442 range;
1443
cristybb503372010-05-27 20:51:26 +00001444 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001445 i;
1446
1447 size_t
cristy9d314ff2011-03-09 01:30:28 +00001448 channels,
cristy49dd6a02011-09-24 23:08:01 +00001449 depth;
cristy3ed852e2009-09-05 21:47:34 +00001450
cristy9d314ff2011-03-09 01:30:28 +00001451 ssize_t
1452 y;
cristy3ed852e2009-09-05 21:47:34 +00001453
1454 assert(image != (Image *) NULL);
1455 assert(image->signature == MagickSignature);
1456 if (image->debug != MagickFalse)
1457 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy49dd6a02011-09-24 23:08:01 +00001458 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1459 MaxPixelChannels+1,sizeof(*channel_statistics));
cristy3ed852e2009-09-05 21:47:34 +00001460 if (channel_statistics == (ChannelStatistics *) NULL)
1461 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy49dd6a02011-09-24 23:08:01 +00001462 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
cristy3ed852e2009-09-05 21:47:34 +00001463 sizeof(*channel_statistics));
cristy25a5f2f2011-09-24 14:09:43 +00001464 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001465 {
1466 channel_statistics[i].depth=1;
1467 channel_statistics[i].maxima=(-1.0E-37);
1468 channel_statistics[i].minima=1.0E+37;
cristy3ed852e2009-09-05 21:47:34 +00001469 }
cristybb503372010-05-27 20:51:26 +00001470 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001471 {
cristy4c08aed2011-07-01 19:47:50 +00001472 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001473 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001474
cristybb503372010-05-27 20:51:26 +00001475 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001476 x;
1477
1478 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001479 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001480 break;
cristy49dd6a02011-09-24 23:08:01 +00001481 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001482 {
cristy49dd6a02011-09-24 23:08:01 +00001483 register ssize_t
1484 i;
1485
1486 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1487 {
1488 PixelTrait
1489 traits;
1490
1491 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1492 if (traits == UndefinedPixelTrait)
1493 continue;
1494 if (channel_statistics[i].depth != MAGICKCORE_QUANTUM_DEPTH)
1495 {
1496 depth=channel_statistics[i].depth;
1497 range=GetQuantumRange(depth);
1498 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
1499 range) ? MagickTrue : MagickFalse;
1500 if (status != MagickFalse)
1501 {
1502 channel_statistics[i].depth++;
1503 continue;
1504 }
cristy3ed852e2009-09-05 21:47:34 +00001505 }
cristy49dd6a02011-09-24 23:08:01 +00001506 if ((double) p[i] < channel_statistics[i].minima)
1507 channel_statistics[i].minima=(double) p[i];
1508 if ((double) p[i] > channel_statistics[i].maxima)
1509 channel_statistics[i].maxima=(double) p[i];
1510 channel_statistics[i].sum+=p[i];
1511 channel_statistics[i].sum_squared+=(double) p[i]*p[i];
1512 channel_statistics[i].sum_cubed+=(double) p[i]*p[i]*p[i];
1513 channel_statistics[i].sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
1514 }
cristyed231572011-07-14 02:18:59 +00001515 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001516 }
1517 }
1518 area=(double) image->columns*image->rows;
cristy25a5f2f2011-09-24 14:09:43 +00001519 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001520 {
cristyfd9dcd42010-08-08 18:07:02 +00001521 channel_statistics[i].sum/=area;
1522 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001523 channel_statistics[i].sum_cubed/=area;
1524 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001525 channel_statistics[i].mean=channel_statistics[i].sum;
1526 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1527 channel_statistics[i].standard_deviation=sqrt(
1528 channel_statistics[i].variance-(channel_statistics[i].mean*
1529 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001530 }
cristy25a5f2f2011-09-24 14:09:43 +00001531 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001532 {
cristy25a5f2f2011-09-24 14:09:43 +00001533 channel_statistics[MaxPixelChannels].depth=(size_t) MagickMax((double)
1534 channel_statistics[MaxPixelChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001535 channel_statistics[i].depth);
cristy25a5f2f2011-09-24 14:09:43 +00001536 channel_statistics[MaxPixelChannels].minima=MagickMin(
1537 channel_statistics[MaxPixelChannels].minima,
cristy32daba42011-05-01 02:34:39 +00001538 channel_statistics[i].minima);
cristy25a5f2f2011-09-24 14:09:43 +00001539 channel_statistics[MaxPixelChannels].maxima=MagickMax(
1540 channel_statistics[MaxPixelChannels].maxima,
cristy32daba42011-05-01 02:34:39 +00001541 channel_statistics[i].maxima);
cristy25a5f2f2011-09-24 14:09:43 +00001542 channel_statistics[MaxPixelChannels].sum+=channel_statistics[i].sum;
1543 channel_statistics[MaxPixelChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001544 channel_statistics[i].sum_squared;
cristy25a5f2f2011-09-24 14:09:43 +00001545 channel_statistics[MaxPixelChannels].sum_cubed+=
cristy32daba42011-05-01 02:34:39 +00001546 channel_statistics[i].sum_cubed;
cristy25a5f2f2011-09-24 14:09:43 +00001547 channel_statistics[MaxPixelChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001548 channel_statistics[i].sum_fourth_power;
cristy25a5f2f2011-09-24 14:09:43 +00001549 channel_statistics[MaxPixelChannels].mean+=channel_statistics[i].mean;
1550 channel_statistics[MaxPixelChannels].variance+=
cristy32daba42011-05-01 02:34:39 +00001551 channel_statistics[i].variance-channel_statistics[i].mean*
1552 channel_statistics[i].mean;
cristy25a5f2f2011-09-24 14:09:43 +00001553 channel_statistics[MaxPixelChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001554 channel_statistics[i].variance-channel_statistics[i].mean*
1555 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001556 }
cristy49dd6a02011-09-24 23:08:01 +00001557 channels=GetImageChannels(image);
cristy25a5f2f2011-09-24 14:09:43 +00001558 channel_statistics[MaxPixelChannels].sum/=channels;
1559 channel_statistics[MaxPixelChannels].sum_squared/=channels;
1560 channel_statistics[MaxPixelChannels].sum_cubed/=channels;
1561 channel_statistics[MaxPixelChannels].sum_fourth_power/=channels;
1562 channel_statistics[MaxPixelChannels].mean/=channels;
1563 channel_statistics[MaxPixelChannels].variance/=channels;
1564 channel_statistics[MaxPixelChannels].standard_deviation=
1565 sqrt(channel_statistics[MaxPixelChannels].standard_deviation/channels);
1566 channel_statistics[MaxPixelChannels].kurtosis/=channels;
1567 channel_statistics[MaxPixelChannels].skewness/=channels;
1568 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001569 {
cristy3ed852e2009-09-05 21:47:34 +00001570 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001571 continue;
1572 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1573 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1574 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1575 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1576 channel_statistics[i].standard_deviation*
1577 channel_statistics[i].standard_deviation);
1578 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1579 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1580 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1581 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1582 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1583 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1584 channel_statistics[i].standard_deviation*
1585 channel_statistics[i].standard_deviation*
1586 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001587 }
1588 return(channel_statistics);
1589}