blob: b909ba8f4b8992d37e512877e90391671a3ce26b [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
cristyba18a7a2011-09-25 15:43:43 +0000131typedef struct _WenusInfo
132{
133 MagickRealType
134 channel[MaxPixelChannels];
135} WenusInfo;
136
137static WenusInfo **DestroyPixelThreadSet(WenusInfo **pixels)
cristy316d5172009-09-17 19:31:25 +0000138{
cristybb503372010-05-27 20:51:26 +0000139 register ssize_t
cristy316d5172009-09-17 19:31:25 +0000140 i;
141
cristyba18a7a2011-09-25 15:43:43 +0000142 assert(pixels != (WenusInfo **) NULL);
cristybb503372010-05-27 20:51:26 +0000143 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristyba18a7a2011-09-25 15:43:43 +0000144 if (pixels[i] != (WenusInfo *) NULL)
145 pixels[i]=(WenusInfo *) RelinquishMagickMemory(pixels[i]);
146 pixels=(WenusInfo **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000147 return(pixels);
148}
149
cristyba18a7a2011-09-25 15:43:43 +0000150static WenusInfo **AcquirePixelThreadSet(const Image *image,
cristy08a3d702010-11-28 01:57:36 +0000151 const size_t number_images)
cristy316d5172009-09-17 19:31:25 +0000152{
cristybb503372010-05-27 20:51:26 +0000153 register ssize_t
cristyba18a7a2011-09-25 15:43:43 +0000154 i;
cristy316d5172009-09-17 19:31:25 +0000155
cristyba18a7a2011-09-25 15:43:43 +0000156 WenusInfo
cristy316d5172009-09-17 19:31:25 +0000157 **pixels;
158
cristybb503372010-05-27 20:51:26 +0000159 size_t
cristy08a3d702010-11-28 01:57:36 +0000160 length,
cristy316d5172009-09-17 19:31:25 +0000161 number_threads;
162
163 number_threads=GetOpenMPMaximumThreads();
cristyba18a7a2011-09-25 15:43:43 +0000164 pixels=(WenusInfo **) AcquireQuantumMemory(number_threads,sizeof(*pixels));
165 if (pixels == (WenusInfo **) NULL)
166 return((WenusInfo **) 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 {
cristyba18a7a2011-09-25 15:43:43 +0000170 register ssize_t
171 j;
172
cristy08a3d702010-11-28 01:57:36 +0000173 length=image->columns;
174 if (length < number_images)
175 length=number_images;
cristyba18a7a2011-09-25 15:43:43 +0000176 pixels[i]=(WenusInfo *) AcquireQuantumMemory(length,sizeof(**pixels));
177 if (pixels[i] == (WenusInfo *) NULL)
cristy316d5172009-09-17 19:31:25 +0000178 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000179 for (j=0; j < (ssize_t) length; j++)
cristyba18a7a2011-09-25 15:43:43 +0000180 {
181 register ssize_t
182 k;
183
184 for (k=0; k < MaxPixelChannels; k++)
185 pixels[i][j].channel[k]=0.0;
186 }
cristy316d5172009-09-17 19:31:25 +0000187 }
188 return(pixels);
189}
190
cristy351842f2010-03-07 15:27:38 +0000191static inline double MagickMax(const double x,const double y)
192{
193 if (x > y)
194 return(x);
195 return(y);
196}
197
cristy08a3d702010-11-28 01:57:36 +0000198#if defined(__cplusplus) || defined(c_plusplus)
199extern "C" {
200#endif
201
202static int IntensityCompare(const void *x,const void *y)
203{
cristyba18a7a2011-09-25 15:43:43 +0000204 const WenusInfo
cristy08a3d702010-11-28 01:57:36 +0000205 *color_1,
206 *color_2;
207
cristyba18a7a2011-09-25 15:43:43 +0000208 MagickRealType
209 distance;
cristy08a3d702010-11-28 01:57:36 +0000210
cristyba18a7a2011-09-25 15:43:43 +0000211 register ssize_t
212 i;
213
214 color_1=(const WenusInfo *) x;
215 color_2=(const WenusInfo *) y;
216 distance=0.0;
217 for (i=0; i < MaxPixelChannels; i++)
218 distance+=color_1->channel[i]-(MagickRealType) color_2->channel[i];
219 return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
cristy08a3d702010-11-28 01:57:36 +0000220}
221
222#if defined(__cplusplus) || defined(c_plusplus)
223}
224#endif
225
cristy351842f2010-03-07 15:27:38 +0000226static inline double MagickMin(const double x,const double y)
227{
228 if (x < y)
229 return(x);
230 return(y);
231}
232
cristyd18ae7c2010-03-07 17:39:52 +0000233static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
234 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000235{
236 MagickRealType
237 result;
238
239 result=0.0;
240 switch (op)
241 {
242 case UndefinedEvaluateOperator:
243 break;
cristy33aaed22010-08-11 18:10:50 +0000244 case AbsEvaluateOperator:
245 {
246 result=(MagickRealType) fabs((double) (pixel+value));
247 break;
248 }
cristy351842f2010-03-07 15:27:38 +0000249 case AddEvaluateOperator:
250 {
251 result=(MagickRealType) (pixel+value);
252 break;
253 }
254 case AddModulusEvaluateOperator:
255 {
256 /*
257 This returns a 'floored modulus' of the addition which is a
cristy49dd6a02011-09-24 23:08:01 +0000258 positive result. It differs from % or fmod() that returns a
259 'truncated modulus' result, where floor() is replaced by trunc() and
260 could return a negative result (which is clipped).
cristy351842f2010-03-07 15:27:38 +0000261 */
262 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000263 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000264 break;
265 }
266 case AndEvaluateOperator:
267 {
cristy9fe8cd72010-10-19 01:24:07 +0000268 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000269 break;
270 }
271 case CosineEvaluateOperator:
272 {
273 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
274 QuantumScale*pixel*value))+0.5));
275 break;
276 }
277 case DivideEvaluateOperator:
278 {
279 result=pixel/(value == 0.0 ? 1.0 : value);
280 break;
281 }
cristy9fe8cd72010-10-19 01:24:07 +0000282 case ExponentialEvaluateOperator:
283 {
284 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
285 pixel)));
286 break;
287 }
cristy351842f2010-03-07 15:27:38 +0000288 case GaussianNoiseEvaluateOperator:
289 {
290 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
291 GaussianNoise,value);
292 break;
293 }
294 case ImpulseNoiseEvaluateOperator:
295 {
296 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
297 ImpulseNoise,value);
298 break;
299 }
300 case LaplacianNoiseEvaluateOperator:
301 {
302 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
303 LaplacianNoise,value);
304 break;
305 }
306 case LeftShiftEvaluateOperator:
307 {
cristy9fe8cd72010-10-19 01:24:07 +0000308 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000309 break;
310 }
311 case LogEvaluateOperator:
312 {
313 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
314 pixel+1.0))/log((double) (value+1.0)));
315 break;
316 }
317 case MaxEvaluateOperator:
318 {
319 result=(MagickRealType) MagickMax((double) pixel,value);
320 break;
321 }
322 case MeanEvaluateOperator:
323 {
cristy125a5a32010-05-07 13:30:52 +0000324 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000325 break;
326 }
cristy08a3d702010-11-28 01:57:36 +0000327 case MedianEvaluateOperator:
328 {
329 result=(MagickRealType) (pixel+value);
330 break;
331 }
cristy351842f2010-03-07 15:27:38 +0000332 case MinEvaluateOperator:
333 {
334 result=(MagickRealType) MagickMin((double) pixel,value);
335 break;
336 }
337 case MultiplicativeNoiseEvaluateOperator:
338 {
339 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
340 MultiplicativeGaussianNoise,value);
341 break;
342 }
343 case MultiplyEvaluateOperator:
344 {
345 result=(MagickRealType) (value*pixel);
346 break;
347 }
348 case OrEvaluateOperator:
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 PoissonNoiseEvaluateOperator:
354 {
355 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
356 PoissonNoise,value);
357 break;
358 }
359 case PowEvaluateOperator:
360 {
361 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
362 (double) value));
363 break;
364 }
365 case RightShiftEvaluateOperator:
366 {
cristy9fe8cd72010-10-19 01:24:07 +0000367 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000368 break;
369 }
370 case SetEvaluateOperator:
371 {
372 result=value;
373 break;
374 }
375 case SineEvaluateOperator:
376 {
377 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
378 QuantumScale*pixel*value))+0.5));
379 break;
380 }
381 case SubtractEvaluateOperator:
382 {
383 result=(MagickRealType) (pixel-value);
384 break;
385 }
386 case ThresholdEvaluateOperator:
387 {
388 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
389 QuantumRange);
390 break;
391 }
392 case ThresholdBlackEvaluateOperator:
393 {
394 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
395 break;
396 }
397 case ThresholdWhiteEvaluateOperator:
398 {
399 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
400 pixel);
401 break;
402 }
403 case UniformNoiseEvaluateOperator:
404 {
405 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
406 UniformNoise,value);
407 break;
408 }
409 case XorEvaluateOperator:
410 {
cristy9fe8cd72010-10-19 01:24:07 +0000411 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000412 break;
413 }
414 }
cristyd18ae7c2010-03-07 17:39:52 +0000415 return(result);
cristy351842f2010-03-07 15:27:38 +0000416}
417
cristyd18ae7c2010-03-07 17:39:52 +0000418MagickExport Image *EvaluateImages(const Image *images,
419 const MagickEvaluateOperator op,ExceptionInfo *exception)
420{
421#define EvaluateImageTag "Evaluate/Image"
422
423 CacheView
424 *evaluate_view;
425
426 const Image
427 *next;
428
429 Image
430 *evaluate_image;
431
cristyd18ae7c2010-03-07 17:39:52 +0000432 MagickBooleanType
433 status;
434
cristy5f959472010-05-27 22:19:46 +0000435 MagickOffsetType
436 progress;
437
cristyba18a7a2011-09-25 15:43:43 +0000438 WenusInfo
439 **restrict evaluate_pixels;
cristyd18ae7c2010-03-07 17:39:52 +0000440
441 RandomInfo
442 **restrict random_info;
443
cristybb503372010-05-27 20:51:26 +0000444 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000445 number_images;
446
cristy5f959472010-05-27 22:19:46 +0000447 ssize_t
448 y;
449
cristyd18ae7c2010-03-07 17:39:52 +0000450 /*
451 Ensure the image are the same size.
452 */
453 assert(images != (Image *) NULL);
454 assert(images->signature == MagickSignature);
455 if (images->debug != MagickFalse)
456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
457 assert(exception != (ExceptionInfo *) NULL);
458 assert(exception->signature == MagickSignature);
459 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
460 if ((next->columns != images->columns) || (next->rows != images->rows))
461 {
462 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
463 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
464 return((Image *) NULL);
465 }
466 /*
467 Initialize evaluate next attributes.
468 */
469 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
470 exception);
471 if (evaluate_image == (Image *) NULL)
472 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000473 if (SetImageStorageClass(evaluate_image,DirectClass,exception) == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000474 {
cristyd18ae7c2010-03-07 17:39:52 +0000475 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);
cristyba18a7a2011-09-25 15:43:43 +0000480 if (evaluate_pixels == (WenusInfo **) 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;
cristyd18ae7c2010-03-07 17:39:52 +0000492 random_info=AcquireRandomInfoThreadSet();
cristyd18ae7c2010-03-07 17:39:52 +0000493 evaluate_view=AcquireCacheView(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000494 if (op == MedianEvaluateOperator)
cristyd18ae7c2010-03-07 17:39:52 +0000495#if defined(MAGICKCORE_OPENMP_SUPPORT)
496 #pragma omp parallel for schedule(dynamic) shared(progress,status)
497#endif
cristy08a3d702010-11-28 01:57:36 +0000498 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristyd18ae7c2010-03-07 17:39:52 +0000499 {
cristy08a3d702010-11-28 01:57:36 +0000500 CacheView
501 *image_view;
cristyd18ae7c2010-03-07 17:39:52 +0000502
cristy08a3d702010-11-28 01:57:36 +0000503 const Image
504 *next;
cristyd18ae7c2010-03-07 17:39:52 +0000505
cristy08a3d702010-11-28 01:57:36 +0000506 const int
507 id = GetOpenMPThreadId();
508
cristyba18a7a2011-09-25 15:43:43 +0000509 register WenusInfo
cristy08a3d702010-11-28 01:57:36 +0000510 *evaluate_pixel;
511
cristy4c08aed2011-07-01 19:47:50 +0000512 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000513 *restrict q;
514
515 register ssize_t
516 x;
517
518 if (status == MagickFalse)
519 continue;
520 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
521 1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000522 if (q == (Quantum *) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000523 {
cristy08a3d702010-11-28 01:57:36 +0000524 status=MagickFalse;
525 continue;
cristyd18ae7c2010-03-07 17:39:52 +0000526 }
cristy08a3d702010-11-28 01:57:36 +0000527 evaluate_pixel=evaluate_pixels[id];
cristybb503372010-05-27 20:51:26 +0000528 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy125a5a32010-05-07 13:30:52 +0000529 {
cristy08a3d702010-11-28 01:57:36 +0000530 register ssize_t
cristyba18a7a2011-09-25 15:43:43 +0000531 j,
532 k;
cristy08a3d702010-11-28 01:57:36 +0000533
cristyba18a7a2011-09-25 15:43:43 +0000534 for (j=0; j < (ssize_t) number_images; j++)
535 for (k=0; k < MaxPixelChannels; k++)
536 evaluate_pixel[j].channel[k]=0.0;
cristy08a3d702010-11-28 01:57:36 +0000537 next=images;
cristyba18a7a2011-09-25 15:43:43 +0000538 for (j=0; j < (ssize_t) number_images; j++)
cristy08a3d702010-11-28 01:57:36 +0000539 {
cristy4c08aed2011-07-01 19:47:50 +0000540 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000541 *p;
542
cristyba18a7a2011-09-25 15:43:43 +0000543 register ssize_t
544 i;
545
cristy08a3d702010-11-28 01:57:36 +0000546 image_view=AcquireCacheView(next);
547 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000548 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000549 {
550 image_view=DestroyCacheView(image_view);
551 break;
552 }
cristyba18a7a2011-09-25 15:43:43 +0000553 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
554 {
555 PixelChannel
556 channel;
557
558 PixelTrait
559 evaluate_traits,
560 traits;
561
562 evaluate_traits=GetPixelChannelMapTraits(evaluate_image,
563 (PixelChannel) i);
564 channel=GetPixelChannelMapChannel(evaluate_image,(PixelChannel) i);
565 traits=GetPixelChannelMapTraits(next,channel);
566 if ((traits == UndefinedPixelTrait) ||
567 (evaluate_traits == UndefinedPixelTrait))
568 continue;
569 if ((evaluate_traits & UpdatePixelTrait) == 0)
570 continue;
571 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(random_info[id],
cristy0beccfa2011-09-25 20:47:53 +0000572 GetPixelChannel(evaluate_image,channel,p),op,
573 evaluate_pixel[j].channel[i]);
cristyba18a7a2011-09-25 15:43:43 +0000574 }
cristy08a3d702010-11-28 01:57:36 +0000575 image_view=DestroyCacheView(image_view);
576 next=GetNextImageInList(next);
577 }
578 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
579 IntensityCompare);
cristyba18a7a2011-09-25 15:43:43 +0000580 for (k=0; k < (ssize_t) GetPixelChannels(evaluate_image); k++)
581 q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]);
cristyed231572011-07-14 02:18:59 +0000582 q+=GetPixelChannels(evaluate_image);
cristy125a5a32010-05-07 13:30:52 +0000583 }
cristy08a3d702010-11-28 01:57:36 +0000584 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
585 status=MagickFalse;
586 if (images->progress_monitor != (MagickProgressMonitor) NULL)
587 {
588 MagickBooleanType
589 proceed;
cristyd18ae7c2010-03-07 17:39:52 +0000590
591#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy08a3d702010-11-28 01:57:36 +0000592 #pragma omp critical (MagickCore_EvaluateImages)
cristyd18ae7c2010-03-07 17:39:52 +0000593#endif
cristy08a3d702010-11-28 01:57:36 +0000594 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
595 evaluate_image->rows);
596 if (proceed == MagickFalse)
597 status=MagickFalse;
598 }
599 }
600 else
601#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyba18a7a2011-09-25 15:43:43 +0000602 #pragma omp parallel for schedule(dynamic) shared(progress,status)
cristy08a3d702010-11-28 01:57:36 +0000603#endif
604 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
605 {
606 CacheView
607 *image_view;
608
609 const Image
610 *next;
611
612 const int
613 id = GetOpenMPThreadId();
614
cristy08a3d702010-11-28 01:57:36 +0000615 register ssize_t
616 i,
617 x;
618
cristyba18a7a2011-09-25 15:43:43 +0000619 register WenusInfo
cristy08a3d702010-11-28 01:57:36 +0000620 *evaluate_pixel;
621
cristy4c08aed2011-07-01 19:47:50 +0000622 register Quantum
cristy08a3d702010-11-28 01:57:36 +0000623 *restrict q;
624
cristyba18a7a2011-09-25 15:43:43 +0000625 ssize_t
626 j;
627
cristy08a3d702010-11-28 01:57:36 +0000628 if (status == MagickFalse)
629 continue;
630 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,evaluate_image->columns,
631 1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000632 if (q == (Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000633 {
cristyd18ae7c2010-03-07 17:39:52 +0000634 status=MagickFalse;
cristy08a3d702010-11-28 01:57:36 +0000635 continue;
636 }
cristy08a3d702010-11-28 01:57:36 +0000637 evaluate_pixel=evaluate_pixels[id];
cristyba18a7a2011-09-25 15:43:43 +0000638 for (j=0; j < (ssize_t) evaluate_image->columns; j++)
639 for (i=0; i < MaxPixelChannels; i++)
640 evaluate_pixel[j].channel[i]=0.0;
cristy08a3d702010-11-28 01:57:36 +0000641 next=images;
cristyba18a7a2011-09-25 15:43:43 +0000642 for (j=0; j < (ssize_t) number_images; j++)
cristy08a3d702010-11-28 01:57:36 +0000643 {
cristy4c08aed2011-07-01 19:47:50 +0000644 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000645 *p;
646
647 image_view=AcquireCacheView(next);
648 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000649 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000650 {
651 image_view=DestroyCacheView(image_view);
652 break;
653 }
cristy08a3d702010-11-28 01:57:36 +0000654 for (x=0; x < (ssize_t) next->columns; x++)
655 {
cristyba18a7a2011-09-25 15:43:43 +0000656 register ssize_t
657 i;
658
659 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
660 {
661 PixelChannel
662 channel;
663
664 PixelTrait
665 evaluate_traits,
666 traits;
667
668 evaluate_traits=GetPixelChannelMapTraits(evaluate_image,
669 (PixelChannel) i);
670 channel=GetPixelChannelMapChannel(evaluate_image,(PixelChannel) i);
671 traits=GetPixelChannelMapTraits(next,channel);
672 if ((traits == UndefinedPixelTrait) ||
673 (evaluate_traits == UndefinedPixelTrait))
674 continue;
675 if ((traits & UpdatePixelTrait) == 0)
676 continue;
677 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(random_info[id],
cristy0beccfa2011-09-25 20:47:53 +0000678 GetPixelChannel(evaluate_image,channel,p),j == 0 ?
679 AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
cristyba18a7a2011-09-25 15:43:43 +0000680 }
cristyed231572011-07-14 02:18:59 +0000681 p+=GetPixelChannels(next);
cristy08a3d702010-11-28 01:57:36 +0000682 }
683 image_view=DestroyCacheView(image_view);
684 next=GetNextImageInList(next);
cristyd18ae7c2010-03-07 17:39:52 +0000685 }
cristy9ee911c2011-09-28 22:33:09 +0000686 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
687 {
688 register ssize_t
689 i;
cristyba18a7a2011-09-25 15:43:43 +0000690
cristy9ee911c2011-09-28 22:33:09 +0000691 switch (op)
692 {
693 case MeanEvaluateOperator:
694 {
695 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
696 evaluate_pixel[x].channel[i]/=(MagickRealType) number_images;
697 break;
698 }
699 case MultiplyEvaluateOperator:
700 {
701 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
702 {
703 register ssize_t
704 j;
705
706 for (j=0; j < (ssize_t) (number_images-1); j++)
707 evaluate_pixel[x].channel[i]*=QuantumScale;
708 }
709 break;
710 }
cristyd02b41a2011-09-28 23:01:59 +0000711 default:
712 break;
cristy08a3d702010-11-28 01:57:36 +0000713 }
cristy9ee911c2011-09-28 22:33:09 +0000714 }
cristy08a3d702010-11-28 01:57:36 +0000715 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
716 {
cristyba18a7a2011-09-25 15:43:43 +0000717 register ssize_t
718 i;
719
720 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
721 {
722 PixelTrait
723 traits;
724
725 traits=GetPixelChannelMapTraits(evaluate_image,(PixelChannel) i);
726 if (traits == UndefinedPixelTrait)
727 continue;
728 if ((traits & UpdatePixelTrait) == 0)
729 continue;
730 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
731 }
cristyed231572011-07-14 02:18:59 +0000732 q+=GetPixelChannels(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000733 }
734 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
735 status=MagickFalse;
736 if (images->progress_monitor != (MagickProgressMonitor) NULL)
737 {
738 MagickBooleanType
739 proceed;
740
741#if defined(MAGICKCORE_OPENMP_SUPPORT)
742 #pragma omp critical (MagickCore_EvaluateImages)
743#endif
744 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
745 evaluate_image->rows);
746 if (proceed == MagickFalse)
747 status=MagickFalse;
748 }
749 }
cristyd18ae7c2010-03-07 17:39:52 +0000750 evaluate_view=DestroyCacheView(evaluate_view);
751 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
752 random_info=DestroyRandomInfoThreadSet(random_info);
753 if (status == MagickFalse)
754 evaluate_image=DestroyImage(evaluate_image);
755 return(evaluate_image);
756}
757
cristyd42d9952011-07-08 14:21:50 +0000758MagickExport MagickBooleanType EvaluateImage(Image *image,
759 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000760{
cristy351842f2010-03-07 15:27:38 +0000761 CacheView
762 *image_view;
763
cristy351842f2010-03-07 15:27:38 +0000764 MagickBooleanType
765 status;
766
cristy5f959472010-05-27 22:19:46 +0000767 MagickOffsetType
768 progress;
769
cristy351842f2010-03-07 15:27:38 +0000770 RandomInfo
771 **restrict random_info;
772
cristy5f959472010-05-27 22:19:46 +0000773 ssize_t
774 y;
775
cristy351842f2010-03-07 15:27:38 +0000776 assert(image != (Image *) NULL);
777 assert(image->signature == MagickSignature);
778 if (image->debug != MagickFalse)
779 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
780 assert(exception != (ExceptionInfo *) NULL);
781 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000782 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
783 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +0000784 status=MagickTrue;
785 progress=0;
786 random_info=AcquireRandomInfoThreadSet();
787 image_view=AcquireCacheView(image);
788#if defined(MAGICKCORE_OPENMP_SUPPORT)
789 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
790#endif
cristybb503372010-05-27 20:51:26 +0000791 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000792 {
cristy5c9e6f22010-09-17 17:31:01 +0000793 const int
794 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000795
cristy4c08aed2011-07-01 19:47:50 +0000796 register Quantum
cristy351842f2010-03-07 15:27:38 +0000797 *restrict q;
798
cristy5c9e6f22010-09-17 17:31:01 +0000799 register ssize_t
800 x;
801
cristy351842f2010-03-07 15:27:38 +0000802 if (status == MagickFalse)
803 continue;
804 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000805 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +0000806 {
807 status=MagickFalse;
808 continue;
809 }
cristybb503372010-05-27 20:51:26 +0000810 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000811 {
cristy49dd6a02011-09-24 23:08:01 +0000812 register ssize_t
813 i;
814
815 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
816 {
817 PixelTrait
818 traits;
819
820 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
821 if (traits == UndefinedPixelTrait)
822 continue;
823 q[i]=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q[i],op,
824 value));
825 }
cristyed231572011-07-14 02:18:59 +0000826 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000827 }
828 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
829 status=MagickFalse;
830 if (image->progress_monitor != (MagickProgressMonitor) NULL)
831 {
832 MagickBooleanType
833 proceed;
834
835#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +0000836 #pragma omp critical (MagickCore_EvaluateImage)
cristy351842f2010-03-07 15:27:38 +0000837#endif
838 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
839 if (proceed == MagickFalse)
840 status=MagickFalse;
841 }
842 }
843 image_view=DestroyCacheView(image_view);
844 random_info=DestroyRandomInfoThreadSet(random_info);
845 return(status);
846}
847
848/*
849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850% %
851% %
852% %
853% F u n c t i o n I m a g e %
854% %
855% %
856% %
857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858%
859% FunctionImage() applies a value to the image with an arithmetic, relational,
860% or logical operator to an image. Use these operations to lighten or darken
861% an image, to increase or decrease contrast in an image, or to produce the
862% "negative" of an image.
863%
cristyd42d9952011-07-08 14:21:50 +0000864% The format of the FunctionImage method is:
cristy351842f2010-03-07 15:27:38 +0000865%
866% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000867% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000868% const double *parameters,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000869%
870% A description of each parameter follows:
871%
872% o image: the image.
873%
cristy351842f2010-03-07 15:27:38 +0000874% o function: A channel function.
875%
876% o parameters: one or more parameters.
877%
878% o exception: return any errors or warnings in this structure.
879%
880*/
881
882static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000883 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000884 ExceptionInfo *exception)
885{
886 MagickRealType
887 result;
888
cristybb503372010-05-27 20:51:26 +0000889 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000890 i;
891
892 (void) exception;
893 result=0.0;
894 switch (function)
895 {
896 case PolynomialFunction:
897 {
898 /*
cristy94a75782011-09-25 02:33:56 +0000899 Polynomial: polynomial constants, highest to lowest order
900 (e.g. c0*x^3 + c1*x^2 + c2*x + c3).
901 */
cristy351842f2010-03-07 15:27:38 +0000902 result=0.0;
cristybb503372010-05-27 20:51:26 +0000903 for (i=0; i < (ssize_t) number_parameters; i++)
cristy94a75782011-09-25 02:33:56 +0000904 result=result*QuantumScale*pixel+parameters[i];
905 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000906 break;
907 }
908 case SinusoidFunction:
909 {
cristy94a75782011-09-25 02:33:56 +0000910 MagickRealType
911 amplitude,
912 bias,
913 frequency,
914 phase;
915
916 /*
917 Sinusoid: frequency, phase, amplitude, bias.
918 */
919 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
920 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
921 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
922 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
923 result=(MagickRealType) (QuantumRange*(amplitude*sin((double) (2.0*
924 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
cristy351842f2010-03-07 15:27:38 +0000925 break;
926 }
927 case ArcsinFunction:
928 {
cristy94a75782011-09-25 02:33:56 +0000929 MagickRealType
930 bias,
931 center,
932 range,
933 width;
934
935 /*
936 Arcsin (peged at range limits for invalid results):
937 width, center, range, and bias.
938 */
939 width=(number_parameters >= 1) ? parameters[0] : 1.0;
940 center=(number_parameters >= 2) ? parameters[1] : 0.5;
941 range=(number_parameters >= 3) ? parameters[2] : 1.0;
942 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
943 result=2.0/width*(QuantumScale*pixel-center);
cristy351842f2010-03-07 15:27:38 +0000944 if ( result <= -1.0 )
cristy94a75782011-09-25 02:33:56 +0000945 result=bias-range/2.0;
cristy351842f2010-03-07 15:27:38 +0000946 else
cristy94a75782011-09-25 02:33:56 +0000947 if (result >= 1.0)
948 result=bias+range/2.0;
949 else
950 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
951 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000952 break;
953 }
954 case ArctanFunction:
955 {
cristy94a75782011-09-25 02:33:56 +0000956 MagickRealType
957 center,
958 bias,
959 range,
960 slope;
961
962 /*
963 Arctan: slope, center, range, and bias.
964 */
965 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
966 center=(number_parameters >= 2) ? parameters[1] : 0.5;
967 range=(number_parameters >= 3) ? parameters[2] : 1.0;
968 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000969 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000970 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
cristy94a75782011-09-25 02:33:56 +0000971 result)+bias));
cristy351842f2010-03-07 15:27:38 +0000972 break;
973 }
974 case UndefinedFunction:
975 break;
976 }
977 return(ClampToQuantum(result));
978}
979
980MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000981 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000982 const double *parameters,ExceptionInfo *exception)
983{
cristy351842f2010-03-07 15:27:38 +0000984#define FunctionImageTag "Function/Image "
985
986 CacheView
987 *image_view;
988
cristy351842f2010-03-07 15:27:38 +0000989 MagickBooleanType
990 status;
991
cristy5f959472010-05-27 22:19:46 +0000992 MagickOffsetType
993 progress;
994
995 ssize_t
996 y;
997
cristy351842f2010-03-07 15:27:38 +0000998 assert(image != (Image *) NULL);
999 assert(image->signature == MagickSignature);
1000 if (image->debug != MagickFalse)
1001 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1002 assert(exception != (ExceptionInfo *) NULL);
1003 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +00001004 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1005 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +00001006 status=MagickTrue;
1007 progress=0;
1008 image_view=AcquireCacheView(image);
1009#if defined(MAGICKCORE_OPENMP_SUPPORT)
1010 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1011#endif
cristybb503372010-05-27 20:51:26 +00001012 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +00001013 {
cristy4c08aed2011-07-01 19:47:50 +00001014 register Quantum
cristy351842f2010-03-07 15:27:38 +00001015 *restrict q;
1016
cristy94a75782011-09-25 02:33:56 +00001017 register ssize_t
1018 x;
1019
cristy351842f2010-03-07 15:27:38 +00001020 if (status == MagickFalse)
1021 continue;
1022 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001023 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +00001024 {
1025 status=MagickFalse;
1026 continue;
1027 }
cristybb503372010-05-27 20:51:26 +00001028 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001029 {
cristy94a75782011-09-25 02:33:56 +00001030 register ssize_t
1031 i;
1032
1033 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1034 {
1035 PixelTrait
1036 traits;
1037
1038 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1039 if (traits == UndefinedPixelTrait)
1040 continue;
1041 if ((traits & UpdatePixelTrait) == 0)
1042 continue;
1043 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1044 exception);
1045 }
cristyed231572011-07-14 02:18:59 +00001046 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +00001047 }
1048 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1049 status=MagickFalse;
1050 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1051 {
1052 MagickBooleanType
1053 proceed;
1054
1055#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +00001056 #pragma omp critical (MagickCore_FunctionImage)
cristy351842f2010-03-07 15:27:38 +00001057#endif
1058 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1059 if (proceed == MagickFalse)
1060 status=MagickFalse;
1061 }
1062 }
1063 image_view=DestroyCacheView(image_view);
1064 return(status);
1065}
1066
1067/*
1068%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069% %
1070% %
1071% %
cristyd42d9952011-07-08 14:21:50 +00001072% G e t I m a g e E x t r e m a %
cristy3ed852e2009-09-05 21:47:34 +00001073% %
1074% %
1075% %
1076%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077%
cristyd42d9952011-07-08 14:21:50 +00001078% GetImageExtrema() returns the extrema of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001079%
cristyd42d9952011-07-08 14:21:50 +00001080% The format of the GetImageExtrema method is:
cristy3ed852e2009-09-05 21:47:34 +00001081%
cristyd42d9952011-07-08 14:21:50 +00001082% MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1083% size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001084%
1085% A description of each parameter follows:
1086%
1087% o image: the image.
1088%
cristy3ed852e2009-09-05 21:47:34 +00001089% o minima: the minimum value in the channel.
1090%
1091% o maxima: the maximum value in the channel.
1092%
1093% o exception: return any errors or warnings in this structure.
1094%
1095*/
cristy3ed852e2009-09-05 21:47:34 +00001096MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001097 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001098{
cristy3ed852e2009-09-05 21:47:34 +00001099 double
1100 max,
1101 min;
1102
1103 MagickBooleanType
1104 status;
1105
1106 assert(image != (Image *) NULL);
1107 assert(image->signature == MagickSignature);
1108 if (image->debug != MagickFalse)
1109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001110 status=GetImageRange(image,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001111 *minima=(size_t) ceil(min-0.5);
1112 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001113 return(status);
1114}
1115
1116/*
1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118% %
1119% %
1120% %
cristyd42d9952011-07-08 14:21:50 +00001121% G e t I m a g e M e a n %
cristy3ed852e2009-09-05 21:47:34 +00001122% %
1123% %
1124% %
1125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126%
cristyd42d9952011-07-08 14:21:50 +00001127% GetImageMean() returns the mean and standard deviation of one or more
cristy3ed852e2009-09-05 21:47:34 +00001128% image channels.
1129%
cristyd42d9952011-07-08 14:21:50 +00001130% The format of the GetImageMean method is:
cristy3ed852e2009-09-05 21:47:34 +00001131%
cristyd42d9952011-07-08 14:21:50 +00001132% MagickBooleanType GetImageMean(const Image *image,double *mean,
1133% double *standard_deviation,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001134%
1135% A description of each parameter follows:
1136%
1137% o image: the image.
1138%
cristy3ed852e2009-09-05 21:47:34 +00001139% o mean: the average value in the channel.
1140%
1141% o standard_deviation: the standard deviation of the channel.
1142%
1143% o exception: return any errors or warnings in this structure.
1144%
1145*/
cristy3ed852e2009-09-05 21:47:34 +00001146MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1147 double *standard_deviation,ExceptionInfo *exception)
1148{
cristyfd9dcd42010-08-08 18:07:02 +00001149 ChannelStatistics
1150 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001151
cristy94a75782011-09-25 02:33:56 +00001152 register ssize_t
1153 i;
1154
cristyfd9dcd42010-08-08 18:07:02 +00001155 size_t
cristy94a75782011-09-25 02:33:56 +00001156 area;
cristy3ed852e2009-09-05 21:47:34 +00001157
1158 assert(image != (Image *) NULL);
1159 assert(image->signature == MagickSignature);
1160 if (image->debug != MagickFalse)
1161 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001162 channel_statistics=GetImageStatistics(image,exception);
cristyfd9dcd42010-08-08 18:07:02 +00001163 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001164 return(MagickFalse);
cristy94a75782011-09-25 02:33:56 +00001165 area=0;
cristy25a5f2f2011-09-24 14:09:43 +00001166 channel_statistics[MaxPixelChannels].mean=0.0;
1167 channel_statistics[MaxPixelChannels].standard_deviation=0.0;
cristy94a75782011-09-25 02:33:56 +00001168 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1169 {
1170 PixelTrait
1171 traits;
1172
1173 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1174 if (traits == UndefinedPixelTrait)
1175 continue;
1176 if ((traits & UpdatePixelTrait) == 0)
1177 continue;
1178 channel_statistics[MaxPixelChannels].mean+=channel_statistics[i].mean;
1179 channel_statistics[MaxPixelChannels].standard_deviation+=
1180 channel_statistics[i].variance-channel_statistics[i].mean*
1181 channel_statistics[i].mean;
1182 area++;
1183 }
1184 channel_statistics[MaxPixelChannels].mean/=area;
cristy25a5f2f2011-09-24 14:09:43 +00001185 channel_statistics[MaxPixelChannels].standard_deviation=
cristy94a75782011-09-25 02:33:56 +00001186 sqrt(channel_statistics[MaxPixelChannels].standard_deviation/area);
cristy25a5f2f2011-09-24 14:09:43 +00001187 *mean=channel_statistics[MaxPixelChannels].mean;
1188 *standard_deviation=channel_statistics[MaxPixelChannels].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001189 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1190 channel_statistics);
1191 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001192}
1193
1194/*
1195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196% %
1197% %
1198% %
cristyd42d9952011-07-08 14:21:50 +00001199% G e t I m a g e K u r t o s i s %
cristy3ed852e2009-09-05 21:47:34 +00001200% %
1201% %
1202% %
1203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204%
cristyd42d9952011-07-08 14:21:50 +00001205% GetImageKurtosis() returns the kurtosis and skewness of one or more
cristy3ed852e2009-09-05 21:47:34 +00001206% image channels.
1207%
cristyd42d9952011-07-08 14:21:50 +00001208% The format of the GetImageKurtosis method is:
cristy3ed852e2009-09-05 21:47:34 +00001209%
cristyd42d9952011-07-08 14:21:50 +00001210% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1211% double *skewness,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001212%
1213% A description of each parameter follows:
1214%
1215% o image: the image.
1216%
cristy3ed852e2009-09-05 21:47:34 +00001217% o kurtosis: the kurtosis of the channel.
1218%
1219% o skewness: the skewness of the channel.
1220%
1221% o exception: return any errors or warnings in this structure.
1222%
1223*/
cristy3ed852e2009-09-05 21:47:34 +00001224MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1225 double *kurtosis,double *skewness,ExceptionInfo *exception)
1226{
cristy94a75782011-09-25 02:33:56 +00001227 CacheView
1228 *image_view;
1229
cristy3ed852e2009-09-05 21:47:34 +00001230 double
1231 area,
1232 mean,
1233 standard_deviation,
1234 sum_squares,
1235 sum_cubes,
1236 sum_fourth_power;
1237
cristy94a75782011-09-25 02:33:56 +00001238 MagickBooleanType
1239 status;
1240
cristybb503372010-05-27 20:51:26 +00001241 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001242 y;
1243
1244 assert(image != (Image *) NULL);
1245 assert(image->signature == MagickSignature);
1246 if (image->debug != MagickFalse)
1247 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy94a75782011-09-25 02:33:56 +00001248 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001249 *kurtosis=0.0;
1250 *skewness=0.0;
1251 area=0.0;
1252 mean=0.0;
1253 standard_deviation=0.0;
1254 sum_squares=0.0;
1255 sum_cubes=0.0;
1256 sum_fourth_power=0.0;
cristy94a75782011-09-25 02:33:56 +00001257 image_view=AcquireCacheView(image);
1258#if defined(MAGICKCORE_OPENMP_SUPPORT)
1259 #pragma omp parallel for schedule(dynamic) shared(status) omp_throttle(1)
1260#endif
cristybb503372010-05-27 20:51:26 +00001261 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001262 {
cristy4c08aed2011-07-01 19:47:50 +00001263 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001264 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001265
cristybb503372010-05-27 20:51:26 +00001266 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001267 x;
1268
cristy94a75782011-09-25 02:33:56 +00001269 if (status == MagickFalse)
1270 continue;
1271 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001272 if (p == (const Quantum *) NULL)
cristy94a75782011-09-25 02:33:56 +00001273 {
1274 status=MagickFalse;
1275 continue;
1276 }
cristybb503372010-05-27 20:51:26 +00001277 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001278 {
cristy94a75782011-09-25 02:33:56 +00001279 register ssize_t
1280 i;
1281
1282 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1283 {
1284 PixelTrait
1285 traits;
1286
1287 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1288 if (traits == UndefinedPixelTrait)
1289 continue;
1290 if ((traits & UpdatePixelTrait) == 0)
1291 continue;
1292#if defined(MAGICKCORE_OPENMP_SUPPORT)
1293 #pragma omp critical (MagickCore_GetImageKurtosis)
1294#endif
cristy3ed852e2009-09-05 21:47:34 +00001295 {
cristy94a75782011-09-25 02:33:56 +00001296 mean+=p[i];
1297 sum_squares+=(double) p[i]*p[i];
1298 sum_cubes+=(double) p[i]*p[i]*p[i];
1299 sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
cristy3ed852e2009-09-05 21:47:34 +00001300 area++;
1301 }
cristy94a75782011-09-25 02:33:56 +00001302 }
cristyed231572011-07-14 02:18:59 +00001303 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001304 }
1305 }
cristy94a75782011-09-25 02:33:56 +00001306 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001307 if (area != 0.0)
1308 {
1309 mean/=area;
1310 sum_squares/=area;
1311 sum_cubes/=area;
1312 sum_fourth_power/=area;
1313 }
1314 standard_deviation=sqrt(sum_squares-(mean*mean));
1315 if (standard_deviation != 0.0)
1316 {
1317 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1318 3.0*mean*mean*mean*mean;
1319 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1320 standard_deviation;
1321 *kurtosis-=3.0;
1322 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1323 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1324 }
cristy94a75782011-09-25 02:33:56 +00001325 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001326}
cristy46f08202010-01-10 04:04:21 +00001327
cristy3ed852e2009-09-05 21:47:34 +00001328/*
1329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330% %
1331% %
1332% %
cristyd42d9952011-07-08 14:21:50 +00001333% G e t I m a g e R a n g e %
cristy3ed852e2009-09-05 21:47:34 +00001334% %
1335% %
1336% %
1337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1338%
cristyd42d9952011-07-08 14:21:50 +00001339% GetImageRange() returns the range of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001340%
cristyd42d9952011-07-08 14:21:50 +00001341% The format of the GetImageRange method is:
cristy3ed852e2009-09-05 21:47:34 +00001342%
cristyd42d9952011-07-08 14:21:50 +00001343% MagickBooleanType GetImageRange(const Image *image,double *minima,
1344% double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001345%
1346% A description of each parameter follows:
1347%
1348% o image: the image.
1349%
cristy3ed852e2009-09-05 21:47:34 +00001350% o minima: the minimum value in the channel.
1351%
1352% o maxima: the maximum value in the channel.
1353%
1354% o exception: return any errors or warnings in this structure.
1355%
1356*/
cristyd42d9952011-07-08 14:21:50 +00001357MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1358 double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001359{
cristy4a9be682011-09-25 02:02:32 +00001360 CacheView
1361 *image_view;
1362
1363 MagickBooleanType
1364 status;
cristy3ed852e2009-09-05 21:47:34 +00001365
cristy9d314ff2011-03-09 01:30:28 +00001366 ssize_t
1367 y;
1368
cristy3ed852e2009-09-05 21:47:34 +00001369 assert(image != (Image *) NULL);
1370 assert(image->signature == MagickSignature);
1371 if (image->debug != MagickFalse)
1372 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy4a9be682011-09-25 02:02:32 +00001373 status=MagickTrue;
cristyc6ea80d2011-09-26 17:38:15 +00001374 *maxima=(-MagickMaxFloat);
1375 *minima=MagickMaxFloat;
cristy4a9be682011-09-25 02:02:32 +00001376 image_view=AcquireCacheView(image);
1377#if defined(MAGICKCORE_OPENMP_SUPPORT)
1378 #pragma omp parallel for schedule(dynamic) shared(status) omp_throttle(1)
1379#endif
cristybb503372010-05-27 20:51:26 +00001380 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001381 {
cristy4c08aed2011-07-01 19:47:50 +00001382 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001383 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001384
cristybb503372010-05-27 20:51:26 +00001385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001386 x;
1387
cristy4a9be682011-09-25 02:02:32 +00001388 if (status == MagickFalse)
1389 continue;
1390 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001391 if (p == (const Quantum *) NULL)
cristy4a9be682011-09-25 02:02:32 +00001392 {
1393 status=MagickFalse;
1394 continue;
1395 }
cristybb503372010-05-27 20:51:26 +00001396 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001397 {
cristy4a9be682011-09-25 02:02:32 +00001398 register ssize_t
1399 i;
1400
1401 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1402 {
1403 PixelTrait
1404 traits;
1405
1406 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1407 if (traits == UndefinedPixelTrait)
1408 continue;
1409 if ((traits & UpdatePixelTrait) == 0)
1410 continue;
1411#if defined(MAGICKCORE_OPENMP_SUPPORT)
1412 #pragma omp critical (MagickCore_GetImageRange)
1413#endif
cristy3ed852e2009-09-05 21:47:34 +00001414 {
cristyba18a7a2011-09-25 15:43:43 +00001415 if (p[i] < *minima)
cristy4a9be682011-09-25 02:02:32 +00001416 *minima=(double) p[i];
cristyba18a7a2011-09-25 15:43:43 +00001417 if (p[i] > *maxima)
cristy4a9be682011-09-25 02:02:32 +00001418 *maxima=(double) p[i];
cristy3ed852e2009-09-05 21:47:34 +00001419 }
cristy4a9be682011-09-25 02:02:32 +00001420 }
cristyed231572011-07-14 02:18:59 +00001421 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001422 }
1423 }
cristy4a9be682011-09-25 02:02:32 +00001424 image_view=DestroyCacheView(image_view);
1425 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001426}
1427
1428/*
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430% %
1431% %
1432% %
cristyd42d9952011-07-08 14:21:50 +00001433% G e t I m a g e S t a t i s t i c s %
cristy3ed852e2009-09-05 21:47:34 +00001434% %
1435% %
1436% %
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438%
cristyd42d9952011-07-08 14:21:50 +00001439% GetImageStatistics() returns statistics for each channel in the
cristy3ed852e2009-09-05 21:47:34 +00001440% image. The statistics include the channel depth, its minima, maxima, mean,
1441% standard deviation, kurtosis and skewness. You can access the red channel
1442% mean, for example, like this:
1443%
cristyd42d9952011-07-08 14:21:50 +00001444% channel_statistics=GetImageStatistics(image,exception);
cristy49dd6a02011-09-24 23:08:01 +00001445% red_mean=channel_statistics[RedPixelChannel].mean;
cristy3ed852e2009-09-05 21:47:34 +00001446%
1447% Use MagickRelinquishMemory() to free the statistics buffer.
1448%
cristyd42d9952011-07-08 14:21:50 +00001449% The format of the GetImageStatistics method is:
cristy3ed852e2009-09-05 21:47:34 +00001450%
cristyd42d9952011-07-08 14:21:50 +00001451% ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001452% ExceptionInfo *exception)
1453%
1454% A description of each parameter follows:
1455%
1456% o image: the image.
1457%
1458% o exception: return any errors or warnings in this structure.
1459%
1460*/
cristy49dd6a02011-09-24 23:08:01 +00001461
1462static size_t GetImageChannels(const Image *image)
1463{
1464 register ssize_t
1465 i;
1466
1467 size_t
1468 channels;
1469
1470 channels=0;
1471 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1472 {
1473 PixelTrait
1474 traits;
1475
1476 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1477 if ((traits & UpdatePixelTrait) != 0)
1478 channels++;
1479 }
1480 return(channels);
1481}
1482
cristyd42d9952011-07-08 14:21:50 +00001483MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001484 ExceptionInfo *exception)
1485{
1486 ChannelStatistics
1487 *channel_statistics;
1488
1489 double
cristyfd9dcd42010-08-08 18:07:02 +00001490 area;
cristy3ed852e2009-09-05 21:47:34 +00001491
cristy3ed852e2009-09-05 21:47:34 +00001492 MagickStatusType
1493 status;
1494
1495 QuantumAny
1496 range;
1497
cristybb503372010-05-27 20:51:26 +00001498 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001499 i;
1500
1501 size_t
cristy9d314ff2011-03-09 01:30:28 +00001502 channels,
cristy49dd6a02011-09-24 23:08:01 +00001503 depth;
cristy3ed852e2009-09-05 21:47:34 +00001504
cristy9d314ff2011-03-09 01:30:28 +00001505 ssize_t
1506 y;
cristy3ed852e2009-09-05 21:47:34 +00001507
1508 assert(image != (Image *) NULL);
1509 assert(image->signature == MagickSignature);
1510 if (image->debug != MagickFalse)
1511 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy49dd6a02011-09-24 23:08:01 +00001512 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1513 MaxPixelChannels+1,sizeof(*channel_statistics));
cristy3ed852e2009-09-05 21:47:34 +00001514 if (channel_statistics == (ChannelStatistics *) NULL)
1515 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy49dd6a02011-09-24 23:08:01 +00001516 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
cristy3ed852e2009-09-05 21:47:34 +00001517 sizeof(*channel_statistics));
cristy25a5f2f2011-09-24 14:09:43 +00001518 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001519 {
1520 channel_statistics[i].depth=1;
cristyc6ea80d2011-09-26 17:38:15 +00001521 channel_statistics[i].maxima=(-MagickMaxFloat);
1522 channel_statistics[i].minima=MagickMaxFloat;
cristy3ed852e2009-09-05 21:47:34 +00001523 }
cristybb503372010-05-27 20:51:26 +00001524 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001525 {
cristy4c08aed2011-07-01 19:47:50 +00001526 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001527 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001528
cristybb503372010-05-27 20:51:26 +00001529 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001530 x;
1531
1532 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001533 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001534 break;
cristy49dd6a02011-09-24 23:08:01 +00001535 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001536 {
cristy49dd6a02011-09-24 23:08:01 +00001537 register ssize_t
1538 i;
1539
1540 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1541 {
1542 PixelTrait
1543 traits;
1544
1545 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1546 if (traits == UndefinedPixelTrait)
1547 continue;
1548 if (channel_statistics[i].depth != MAGICKCORE_QUANTUM_DEPTH)
1549 {
1550 depth=channel_statistics[i].depth;
1551 range=GetQuantumRange(depth);
1552 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
1553 range) ? MagickTrue : MagickFalse;
1554 if (status != MagickFalse)
1555 {
1556 channel_statistics[i].depth++;
1557 continue;
1558 }
cristy3ed852e2009-09-05 21:47:34 +00001559 }
cristy49dd6a02011-09-24 23:08:01 +00001560 if ((double) p[i] < channel_statistics[i].minima)
1561 channel_statistics[i].minima=(double) p[i];
1562 if ((double) p[i] > channel_statistics[i].maxima)
1563 channel_statistics[i].maxima=(double) p[i];
1564 channel_statistics[i].sum+=p[i];
1565 channel_statistics[i].sum_squared+=(double) p[i]*p[i];
1566 channel_statistics[i].sum_cubed+=(double) p[i]*p[i]*p[i];
1567 channel_statistics[i].sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
1568 }
cristyed231572011-07-14 02:18:59 +00001569 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001570 }
1571 }
1572 area=(double) image->columns*image->rows;
cristy25a5f2f2011-09-24 14:09:43 +00001573 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001574 {
cristyfd9dcd42010-08-08 18:07:02 +00001575 channel_statistics[i].sum/=area;
1576 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001577 channel_statistics[i].sum_cubed/=area;
1578 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001579 channel_statistics[i].mean=channel_statistics[i].sum;
1580 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1581 channel_statistics[i].standard_deviation=sqrt(
1582 channel_statistics[i].variance-(channel_statistics[i].mean*
1583 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001584 }
cristy25a5f2f2011-09-24 14:09:43 +00001585 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001586 {
cristy25a5f2f2011-09-24 14:09:43 +00001587 channel_statistics[MaxPixelChannels].depth=(size_t) MagickMax((double)
1588 channel_statistics[MaxPixelChannels].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001589 channel_statistics[i].depth);
cristy25a5f2f2011-09-24 14:09:43 +00001590 channel_statistics[MaxPixelChannels].minima=MagickMin(
1591 channel_statistics[MaxPixelChannels].minima,
cristy32daba42011-05-01 02:34:39 +00001592 channel_statistics[i].minima);
cristy25a5f2f2011-09-24 14:09:43 +00001593 channel_statistics[MaxPixelChannels].maxima=MagickMax(
1594 channel_statistics[MaxPixelChannels].maxima,
cristy32daba42011-05-01 02:34:39 +00001595 channel_statistics[i].maxima);
cristy25a5f2f2011-09-24 14:09:43 +00001596 channel_statistics[MaxPixelChannels].sum+=channel_statistics[i].sum;
1597 channel_statistics[MaxPixelChannels].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001598 channel_statistics[i].sum_squared;
cristy25a5f2f2011-09-24 14:09:43 +00001599 channel_statistics[MaxPixelChannels].sum_cubed+=
cristy32daba42011-05-01 02:34:39 +00001600 channel_statistics[i].sum_cubed;
cristy25a5f2f2011-09-24 14:09:43 +00001601 channel_statistics[MaxPixelChannels].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001602 channel_statistics[i].sum_fourth_power;
cristy25a5f2f2011-09-24 14:09:43 +00001603 channel_statistics[MaxPixelChannels].mean+=channel_statistics[i].mean;
1604 channel_statistics[MaxPixelChannels].variance+=
cristy32daba42011-05-01 02:34:39 +00001605 channel_statistics[i].variance-channel_statistics[i].mean*
1606 channel_statistics[i].mean;
cristy25a5f2f2011-09-24 14:09:43 +00001607 channel_statistics[MaxPixelChannels].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001608 channel_statistics[i].variance-channel_statistics[i].mean*
1609 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001610 }
cristy49dd6a02011-09-24 23:08:01 +00001611 channels=GetImageChannels(image);
cristy25a5f2f2011-09-24 14:09:43 +00001612 channel_statistics[MaxPixelChannels].sum/=channels;
1613 channel_statistics[MaxPixelChannels].sum_squared/=channels;
1614 channel_statistics[MaxPixelChannels].sum_cubed/=channels;
1615 channel_statistics[MaxPixelChannels].sum_fourth_power/=channels;
1616 channel_statistics[MaxPixelChannels].mean/=channels;
1617 channel_statistics[MaxPixelChannels].variance/=channels;
1618 channel_statistics[MaxPixelChannels].standard_deviation=
1619 sqrt(channel_statistics[MaxPixelChannels].standard_deviation/channels);
1620 channel_statistics[MaxPixelChannels].kurtosis/=channels;
1621 channel_statistics[MaxPixelChannels].skewness/=channels;
1622 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001623 {
cristy3ed852e2009-09-05 21:47:34 +00001624 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001625 continue;
1626 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-
1627 3.0*channel_statistics[i].mean*channel_statistics[i].sum_squared+
1628 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
1629 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1630 channel_statistics[i].standard_deviation*
1631 channel_statistics[i].standard_deviation);
1632 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-
1633 4.0*channel_statistics[i].mean*channel_statistics[i].sum_cubed+
1634 6.0*channel_statistics[i].mean*channel_statistics[i].mean*
1635 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1636 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1637 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1638 channel_statistics[i].standard_deviation*
1639 channel_statistics[i].standard_deviation*
1640 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001641 }
1642 return(channel_statistics);
1643}