blob: 9023de096f6d9019c9308e82441a7bca531f59b7 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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
cristy95a072b2011-10-13 18:03:11 +0000131typedef struct _PixelChannels
cristyba18a7a2011-09-25 15:43:43 +0000132{
133 MagickRealType
cristy5f95f4f2011-10-23 01:01:01 +0000134 channel[CompositePixelChannel];
cristy95a072b2011-10-13 18:03:11 +0000135} PixelChannels;
cristyba18a7a2011-09-25 15:43:43 +0000136
cristy95a072b2011-10-13 18:03:11 +0000137static PixelChannels **DestroyPixelThreadSet(PixelChannels **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
cristy95a072b2011-10-13 18:03:11 +0000142 assert(pixels != (PixelChannels **) NULL);
cristybb503372010-05-27 20:51:26 +0000143 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy95a072b2011-10-13 18:03:11 +0000144 if (pixels[i] != (PixelChannels *) NULL)
145 pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]);
146 pixels=(PixelChannels **) RelinquishMagickMemory(pixels);
cristy316d5172009-09-17 19:31:25 +0000147 return(pixels);
148}
149
cristy95a072b2011-10-13 18:03:11 +0000150static PixelChannels **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
cristy95a072b2011-10-13 18:03:11 +0000156 PixelChannels
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();
cristye2a912b2011-12-05 20:02:07 +0000164 pixels=(PixelChannels **) AcquireQuantumMemory(number_threads,
165 sizeof(*pixels));
cristy95a072b2011-10-13 18:03:11 +0000166 if (pixels == (PixelChannels **) NULL)
167 return((PixelChannels **) NULL);
cristy316d5172009-09-17 19:31:25 +0000168 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000169 for (i=0; i < (ssize_t) number_threads; i++)
cristy316d5172009-09-17 19:31:25 +0000170 {
cristyba18a7a2011-09-25 15:43:43 +0000171 register ssize_t
172 j;
173
cristy08a3d702010-11-28 01:57:36 +0000174 length=image->columns;
175 if (length < number_images)
176 length=number_images;
cristy95a072b2011-10-13 18:03:11 +0000177 pixels[i]=(PixelChannels *) AcquireQuantumMemory(length,sizeof(**pixels));
178 if (pixels[i] == (PixelChannels *) NULL)
cristy316d5172009-09-17 19:31:25 +0000179 return(DestroyPixelThreadSet(pixels));
cristy08a3d702010-11-28 01:57:36 +0000180 for (j=0; j < (ssize_t) length; j++)
cristyba18a7a2011-09-25 15:43:43 +0000181 {
182 register ssize_t
183 k;
184
185 for (k=0; k < MaxPixelChannels; k++)
186 pixels[i][j].channel[k]=0.0;
187 }
cristy316d5172009-09-17 19:31:25 +0000188 }
189 return(pixels);
190}
191
cristy99bd5232011-12-07 14:38:20 +0000192static inline double EvaluateMax(const double x,const double y)
cristy351842f2010-03-07 15:27:38 +0000193{
194 if (x > y)
195 return(x);
196 return(y);
197}
198
cristy08a3d702010-11-28 01:57:36 +0000199#if defined(__cplusplus) || defined(c_plusplus)
200extern "C" {
201#endif
202
203static int IntensityCompare(const void *x,const void *y)
204{
cristy95a072b2011-10-13 18:03:11 +0000205 const PixelChannels
cristy08a3d702010-11-28 01:57:36 +0000206 *color_1,
207 *color_2;
208
cristyba18a7a2011-09-25 15:43:43 +0000209 MagickRealType
210 distance;
cristy08a3d702010-11-28 01:57:36 +0000211
cristyba18a7a2011-09-25 15:43:43 +0000212 register ssize_t
213 i;
214
cristy95a072b2011-10-13 18:03:11 +0000215 color_1=(const PixelChannels *) x;
216 color_2=(const PixelChannels *) y;
cristyba18a7a2011-09-25 15:43:43 +0000217 distance=0.0;
218 for (i=0; i < MaxPixelChannels; i++)
219 distance+=color_1->channel[i]-(MagickRealType) color_2->channel[i];
220 return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
cristy08a3d702010-11-28 01:57:36 +0000221}
222
223#if defined(__cplusplus) || defined(c_plusplus)
224}
225#endif
226
cristy351842f2010-03-07 15:27:38 +0000227static inline double MagickMin(const double x,const double y)
228{
229 if (x < y)
230 return(x);
231 return(y);
232}
233
cristyd18ae7c2010-03-07 17:39:52 +0000234static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
235 Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
cristy351842f2010-03-07 15:27:38 +0000236{
237 MagickRealType
238 result;
239
240 result=0.0;
241 switch (op)
242 {
243 case UndefinedEvaluateOperator:
244 break;
cristy33aaed22010-08-11 18:10:50 +0000245 case AbsEvaluateOperator:
246 {
247 result=(MagickRealType) fabs((double) (pixel+value));
248 break;
249 }
cristy351842f2010-03-07 15:27:38 +0000250 case AddEvaluateOperator:
251 {
252 result=(MagickRealType) (pixel+value);
253 break;
254 }
255 case AddModulusEvaluateOperator:
256 {
257 /*
cristye2a912b2011-12-05 20:02:07 +0000258 This returns a 'floored modulus' of the addition which is a positive
259 result. It differs from % or fmod() that returns a 'truncated modulus'
260 result, where floor() is replaced by trunc() and could return a
261 negative result (which is clipped).
cristy351842f2010-03-07 15:27:38 +0000262 */
263 result=pixel+value;
cristy364a8e22010-05-10 17:06:33 +0000264 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0));
cristy351842f2010-03-07 15:27:38 +0000265 break;
266 }
267 case AndEvaluateOperator:
268 {
cristy9fe8cd72010-10-19 01:24:07 +0000269 result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000270 break;
271 }
272 case CosineEvaluateOperator:
273 {
274 result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
275 QuantumScale*pixel*value))+0.5));
276 break;
277 }
278 case DivideEvaluateOperator:
279 {
280 result=pixel/(value == 0.0 ? 1.0 : value);
281 break;
282 }
cristy9fe8cd72010-10-19 01:24:07 +0000283 case ExponentialEvaluateOperator:
284 {
285 result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
286 pixel)));
287 break;
288 }
cristy351842f2010-03-07 15:27:38 +0000289 case GaussianNoiseEvaluateOperator:
290 {
291 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
292 GaussianNoise,value);
293 break;
294 }
295 case ImpulseNoiseEvaluateOperator:
296 {
297 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
298 ImpulseNoise,value);
299 break;
300 }
301 case LaplacianNoiseEvaluateOperator:
302 {
303 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
304 LaplacianNoise,value);
305 break;
306 }
307 case LeftShiftEvaluateOperator:
308 {
cristy9fe8cd72010-10-19 01:24:07 +0000309 result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000310 break;
311 }
312 case LogEvaluateOperator:
313 {
314 result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
315 pixel+1.0))/log((double) (value+1.0)));
316 break;
317 }
318 case MaxEvaluateOperator:
319 {
cristy99bd5232011-12-07 14:38:20 +0000320 result=(MagickRealType) EvaluateMax((double) pixel,value);
cristy351842f2010-03-07 15:27:38 +0000321 break;
322 }
323 case MeanEvaluateOperator:
324 {
cristy125a5a32010-05-07 13:30:52 +0000325 result=(MagickRealType) (pixel+value);
cristy351842f2010-03-07 15:27:38 +0000326 break;
327 }
cristy08a3d702010-11-28 01:57:36 +0000328 case MedianEvaluateOperator:
329 {
330 result=(MagickRealType) (pixel+value);
331 break;
332 }
cristy351842f2010-03-07 15:27:38 +0000333 case MinEvaluateOperator:
334 {
335 result=(MagickRealType) MagickMin((double) pixel,value);
336 break;
337 }
338 case MultiplicativeNoiseEvaluateOperator:
339 {
340 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
341 MultiplicativeGaussianNoise,value);
342 break;
343 }
344 case MultiplyEvaluateOperator:
345 {
346 result=(MagickRealType) (value*pixel);
347 break;
348 }
349 case OrEvaluateOperator:
350 {
cristy9fe8cd72010-10-19 01:24:07 +0000351 result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000352 break;
353 }
354 case PoissonNoiseEvaluateOperator:
355 {
356 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
357 PoissonNoise,value);
358 break;
359 }
360 case PowEvaluateOperator:
361 {
362 result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
363 (double) value));
364 break;
365 }
366 case RightShiftEvaluateOperator:
367 {
cristy9fe8cd72010-10-19 01:24:07 +0000368 result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000369 break;
370 }
371 case SetEvaluateOperator:
372 {
373 result=value;
374 break;
375 }
376 case SineEvaluateOperator:
377 {
378 result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
379 QuantumScale*pixel*value))+0.5));
380 break;
381 }
382 case SubtractEvaluateOperator:
383 {
384 result=(MagickRealType) (pixel-value);
385 break;
386 }
387 case ThresholdEvaluateOperator:
388 {
389 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
390 QuantumRange);
391 break;
392 }
393 case ThresholdBlackEvaluateOperator:
394 {
395 result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
396 break;
397 }
398 case ThresholdWhiteEvaluateOperator:
399 {
400 result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
401 pixel);
402 break;
403 }
404 case UniformNoiseEvaluateOperator:
405 {
406 result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
407 UniformNoise,value);
408 break;
409 }
410 case XorEvaluateOperator:
411 {
cristy9fe8cd72010-10-19 01:24:07 +0000412 result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
cristy351842f2010-03-07 15:27:38 +0000413 break;
414 }
415 }
cristyd18ae7c2010-03-07 17:39:52 +0000416 return(result);
cristy351842f2010-03-07 15:27:38 +0000417}
418
cristyd18ae7c2010-03-07 17:39:52 +0000419MagickExport Image *EvaluateImages(const Image *images,
420 const MagickEvaluateOperator op,ExceptionInfo *exception)
421{
422#define EvaluateImageTag "Evaluate/Image"
423
424 CacheView
425 *evaluate_view;
426
427 const Image
428 *next;
429
430 Image
431 *evaluate_image;
432
cristyd18ae7c2010-03-07 17:39:52 +0000433 MagickBooleanType
434 status;
435
cristy5f959472010-05-27 22:19:46 +0000436 MagickOffsetType
437 progress;
438
cristy95a072b2011-10-13 18:03:11 +0000439 PixelChannels
cristyba18a7a2011-09-25 15:43:43 +0000440 **restrict evaluate_pixels;
cristyd18ae7c2010-03-07 17:39:52 +0000441
442 RandomInfo
443 **restrict random_info;
444
cristybb503372010-05-27 20:51:26 +0000445 size_t
cristyd18ae7c2010-03-07 17:39:52 +0000446 number_images;
447
cristy5f959472010-05-27 22:19:46 +0000448 ssize_t
449 y;
450
cristyd18ae7c2010-03-07 17:39:52 +0000451 /*
452 Ensure the image are the same size.
453 */
454 assert(images != (Image *) NULL);
455 assert(images->signature == MagickSignature);
456 if (images->debug != MagickFalse)
457 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
458 assert(exception != (ExceptionInfo *) NULL);
459 assert(exception->signature == MagickSignature);
460 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
461 if ((next->columns != images->columns) || (next->rows != images->rows))
462 {
463 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
464 "ImageWidthsOrHeightsDiffer","`%s'",images->filename);
465 return((Image *) NULL);
466 }
467 /*
468 Initialize evaluate next attributes.
469 */
470 evaluate_image=CloneImage(images,images->columns,images->rows,MagickTrue,
471 exception);
472 if (evaluate_image == (Image *) NULL)
473 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +0000474 if (SetImageStorageClass(evaluate_image,DirectClass,exception) == MagickFalse)
cristyd18ae7c2010-03-07 17:39:52 +0000475 {
cristyd18ae7c2010-03-07 17:39:52 +0000476 evaluate_image=DestroyImage(evaluate_image);
477 return((Image *) NULL);
478 }
cristy08a3d702010-11-28 01:57:36 +0000479 number_images=GetImageListLength(images);
480 evaluate_pixels=AcquirePixelThreadSet(images,number_images);
cristy95a072b2011-10-13 18:03:11 +0000481 if (evaluate_pixels == (PixelChannels **) NULL)
cristyd18ae7c2010-03-07 17:39:52 +0000482 {
483 evaluate_image=DestroyImage(evaluate_image);
484 (void) ThrowMagickException(exception,GetMagickModule(),
485 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
486 return((Image *) NULL);
487 }
488 /*
489 Evaluate image pixels.
490 */
491 status=MagickTrue;
492 progress=0;
cristyd18ae7c2010-03-07 17:39:52 +0000493 random_info=AcquireRandomInfoThreadSet();
cristyd18ae7c2010-03-07 17:39:52 +0000494 evaluate_view=AcquireCacheView(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000495 if (op == MedianEvaluateOperator)
cristyd18ae7c2010-03-07 17:39:52 +0000496 {
cristye2a912b2011-12-05 20:02:07 +0000497#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000498 #pragma omp parallel for schedule(static) shared(progress,status)
cristya30fec62011-11-21 17:42:27 +0000499#endif
500 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
cristy125a5a32010-05-07 13:30:52 +0000501 {
cristya30fec62011-11-21 17:42:27 +0000502 CacheView
503 *image_view;
cristy08a3d702010-11-28 01:57:36 +0000504
cristya30fec62011-11-21 17:42:27 +0000505 const Image
506 *next;
507
508 const int
509 id = GetOpenMPThreadId();
510
511 register PixelChannels
512 *evaluate_pixel;
513
514 register Quantum
515 *restrict q;
516
517 register ssize_t
518 x;
519
520 if (status == MagickFalse)
521 continue;
522 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,
523 evaluate_image->columns,1,exception);
524 if (q == (Quantum *) NULL)
525 {
526 status=MagickFalse;
527 continue;
528 }
529 evaluate_pixel=evaluate_pixels[id];
530 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
531 {
532 register ssize_t
533 j,
534 k;
535
536 for (j=0; j < (ssize_t) number_images; j++)
537 for (k=0; k < MaxPixelChannels; k++)
538 evaluate_pixel[j].channel[k]=0.0;
539 next=images;
540 for (j=0; j < (ssize_t) number_images; j++)
541 {
542 register const Quantum
543 *p;
544
545 register ssize_t
546 i;
547
548 image_view=AcquireCacheView(next);
549 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception);
550 if (p == (const Quantum *) NULL)
551 {
552 image_view=DestroyCacheView(image_view);
553 break;
554 }
555 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
556 {
557 PixelChannel
558 channel;
559
560 PixelTrait
561 evaluate_traits,
562 traits;
563
cristyabace412011-12-11 15:56:53 +0000564 channel=GetPixelChannelMapChannel(evaluate_image,i);
565 evaluate_traits=GetPixelChannelMapTraits(evaluate_image,channel);
cristya30fec62011-11-21 17:42:27 +0000566 traits=GetPixelChannelMapTraits(next,channel);
567 if ((traits == UndefinedPixelTrait) ||
568 (evaluate_traits == UndefinedPixelTrait))
569 continue;
570 if ((evaluate_traits & UpdatePixelTrait) == 0)
571 continue;
572 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator(
573 random_info[id],GetPixelChannel(evaluate_image,channel,p),op,
574 evaluate_pixel[j].channel[i]);
575 }
576 image_view=DestroyCacheView(image_view);
577 next=GetNextImageInList(next);
578 }
579 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel),
580 IntensityCompare);
581 for (k=0; k < (ssize_t) GetPixelChannels(evaluate_image); k++)
582 q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]);
583 q+=GetPixelChannels(evaluate_image);
584 }
585 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
586 status=MagickFalse;
587 if (images->progress_monitor != (MagickProgressMonitor) NULL)
588 {
589 MagickBooleanType
590 proceed;
591
592#if defined(MAGICKCORE_OPENMP_SUPPORT)
593 #pragma omp critical (MagickCore_EvaluateImages)
594#endif
595 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
596 evaluate_image->rows);
597 if (proceed == MagickFalse)
598 status=MagickFalse;
599 }
600 }
601 }
602 else
603 {
604#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000605 #pragma omp parallel for schedule(static) shared(progress,status)
cristya30fec62011-11-21 17:42:27 +0000606#endif
607 for (y=0; y < (ssize_t) evaluate_image->rows; y++)
608 {
609 CacheView
610 *image_view;
611
612 const Image
613 *next;
614
615 const int
616 id = GetOpenMPThreadId();
617
618 register ssize_t
619 i,
620 x;
621
622 register PixelChannels
623 *evaluate_pixel;
624
625 register Quantum
626 *restrict q;
627
628 ssize_t
629 j;
630
631 if (status == MagickFalse)
632 continue;
633 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,
634 evaluate_image->columns,1,exception);
635 if (q == (Quantum *) NULL)
636 {
637 status=MagickFalse;
638 continue;
639 }
640 evaluate_pixel=evaluate_pixels[id];
641 for (j=0; j < (ssize_t) evaluate_image->columns; j++)
642 for (i=0; i < MaxPixelChannels; i++)
643 evaluate_pixel[j].channel[i]=0.0;
cristy08a3d702010-11-28 01:57:36 +0000644 next=images;
cristyba18a7a2011-09-25 15:43:43 +0000645 for (j=0; j < (ssize_t) number_images; j++)
cristy08a3d702010-11-28 01:57:36 +0000646 {
cristy4c08aed2011-07-01 19:47:50 +0000647 register const Quantum
cristy08a3d702010-11-28 01:57:36 +0000648 *p;
649
650 image_view=AcquireCacheView(next);
cristya30fec62011-11-21 17:42:27 +0000651 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000652 if (p == (const Quantum *) NULL)
cristy08a3d702010-11-28 01:57:36 +0000653 {
654 image_view=DestroyCacheView(image_view);
655 break;
656 }
cristya30fec62011-11-21 17:42:27 +0000657 for (x=0; x < (ssize_t) next->columns; x++)
cristyba18a7a2011-09-25 15:43:43 +0000658 {
cristya30fec62011-11-21 17:42:27 +0000659 register ssize_t
660 i;
cristyba18a7a2011-09-25 15:43:43 +0000661
cristya30fec62011-11-21 17:42:27 +0000662 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
663 {
664 PixelChannel
665 channel;
cristyba18a7a2011-09-25 15:43:43 +0000666
cristya30fec62011-11-21 17:42:27 +0000667 PixelTrait
668 evaluate_traits,
669 traits;
670
cristye2a912b2011-12-05 20:02:07 +0000671 channel=GetPixelChannelMapChannel(evaluate_image,i);
cristya30fec62011-11-21 17:42:27 +0000672 traits=GetPixelChannelMapTraits(next,channel);
cristyabace412011-12-11 15:56:53 +0000673 evaluate_traits=GetPixelChannelMapTraits(evaluate_image,channel);
cristya30fec62011-11-21 17:42:27 +0000674 if ((traits == UndefinedPixelTrait) ||
675 (evaluate_traits == UndefinedPixelTrait))
676 continue;
677 if ((traits & UpdatePixelTrait) == 0)
678 continue;
679 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
cristye2a912b2011-12-05 20:02:07 +0000680 random_info[id],GetPixelChannel(evaluate_image,channel,p),j ==
681 0 ? AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
cristya30fec62011-11-21 17:42:27 +0000682 }
683 p+=GetPixelChannels(next);
cristyba18a7a2011-09-25 15:43:43 +0000684 }
cristy08a3d702010-11-28 01:57:36 +0000685 image_view=DestroyCacheView(image_view);
686 next=GetNextImageInList(next);
687 }
cristya30fec62011-11-21 17:42:27 +0000688 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy08a3d702010-11-28 01:57:36 +0000689 {
cristya30fec62011-11-21 17:42:27 +0000690 register ssize_t
691 i;
cristyd18ae7c2010-03-07 17:39:52 +0000692
cristya30fec62011-11-21 17:42:27 +0000693 switch (op)
cristy08a3d702010-11-28 01:57:36 +0000694 {
cristya30fec62011-11-21 17:42:27 +0000695 case MeanEvaluateOperator:
696 {
697 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
698 evaluate_pixel[x].channel[i]/=(MagickRealType) number_images;
699 break;
700 }
701 case MultiplyEvaluateOperator:
702 {
703 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
704 {
705 register ssize_t
706 j;
707
708 for (j=0; j < (ssize_t) (number_images-1); j++)
709 evaluate_pixel[x].channel[i]*=QuantumScale;
710 }
711 break;
712 }
713 default:
714 break;
cristy08a3d702010-11-28 01:57:36 +0000715 }
cristya30fec62011-11-21 17:42:27 +0000716 }
717 for (x=0; x < (ssize_t) evaluate_image->columns; x++)
cristy08a3d702010-11-28 01:57:36 +0000718 {
cristyba18a7a2011-09-25 15:43:43 +0000719 register ssize_t
720 i;
721
722 for (i=0; i < (ssize_t) GetPixelChannels(evaluate_image); i++)
723 {
cristyabace412011-12-11 15:56:53 +0000724 PixelChannel
725 channel;
726
cristyba18a7a2011-09-25 15:43:43 +0000727 PixelTrait
cristyba18a7a2011-09-25 15:43:43 +0000728 traits;
729
cristyabace412011-12-11 15:56:53 +0000730 channel=GetPixelChannelMapChannel(evaluate_image,i);
731 traits=GetPixelChannelMapTraits(evaluate_image,channel);
cristya30fec62011-11-21 17:42:27 +0000732 if (traits == UndefinedPixelTrait)
cristyba18a7a2011-09-25 15:43:43 +0000733 continue;
734 if ((traits & UpdatePixelTrait) == 0)
735 continue;
cristya30fec62011-11-21 17:42:27 +0000736 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]);
cristyba18a7a2011-09-25 15:43:43 +0000737 }
cristya30fec62011-11-21 17:42:27 +0000738 q+=GetPixelChannels(evaluate_image);
cristy08a3d702010-11-28 01:57:36 +0000739 }
cristya30fec62011-11-21 17:42:27 +0000740 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse)
741 status=MagickFalse;
742 if (images->progress_monitor != (MagickProgressMonitor) NULL)
cristy9ee911c2011-09-28 22:33:09 +0000743 {
cristya30fec62011-11-21 17:42:27 +0000744 MagickBooleanType
745 proceed;
cristy9ee911c2011-09-28 22:33:09 +0000746
cristya30fec62011-11-21 17:42:27 +0000747#if defined(MAGICKCORE_OPENMP_SUPPORT)
748 #pragma omp critical (MagickCore_EvaluateImages)
cristy08a3d702010-11-28 01:57:36 +0000749#endif
cristya30fec62011-11-21 17:42:27 +0000750 proceed=SetImageProgress(images,EvaluateImageTag,progress++,
751 evaluate_image->rows);
752 if (proceed == MagickFalse)
753 status=MagickFalse;
754 }
755 }
cristy08a3d702010-11-28 01:57:36 +0000756 }
cristyd18ae7c2010-03-07 17:39:52 +0000757 evaluate_view=DestroyCacheView(evaluate_view);
758 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels);
759 random_info=DestroyRandomInfoThreadSet(random_info);
760 if (status == MagickFalse)
761 evaluate_image=DestroyImage(evaluate_image);
762 return(evaluate_image);
763}
764
cristyd42d9952011-07-08 14:21:50 +0000765MagickExport MagickBooleanType EvaluateImage(Image *image,
766 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000767{
cristy351842f2010-03-07 15:27:38 +0000768 CacheView
769 *image_view;
770
cristy351842f2010-03-07 15:27:38 +0000771 MagickBooleanType
772 status;
773
cristy5f959472010-05-27 22:19:46 +0000774 MagickOffsetType
775 progress;
776
cristy351842f2010-03-07 15:27:38 +0000777 RandomInfo
778 **restrict random_info;
779
cristy5f959472010-05-27 22:19:46 +0000780 ssize_t
781 y;
782
cristy351842f2010-03-07 15:27:38 +0000783 assert(image != (Image *) NULL);
784 assert(image->signature == MagickSignature);
785 if (image->debug != MagickFalse)
786 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
787 assert(exception != (ExceptionInfo *) NULL);
788 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000789 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
790 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +0000791 status=MagickTrue;
792 progress=0;
793 random_info=AcquireRandomInfoThreadSet();
794 image_view=AcquireCacheView(image);
795#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000796 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy351842f2010-03-07 15:27:38 +0000797#endif
cristybb503372010-05-27 20:51:26 +0000798 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +0000799 {
cristy5c9e6f22010-09-17 17:31:01 +0000800 const int
801 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000802
cristy4c08aed2011-07-01 19:47:50 +0000803 register Quantum
cristy351842f2010-03-07 15:27:38 +0000804 *restrict q;
805
cristy5c9e6f22010-09-17 17:31:01 +0000806 register ssize_t
807 x;
808
cristy351842f2010-03-07 15:27:38 +0000809 if (status == MagickFalse)
810 continue;
811 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000812 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +0000813 {
814 status=MagickFalse;
815 continue;
816 }
cristybb503372010-05-27 20:51:26 +0000817 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +0000818 {
cristy49dd6a02011-09-24 23:08:01 +0000819 register ssize_t
820 i;
821
822 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
823 {
cristyabace412011-12-11 15:56:53 +0000824 PixelChannel
825 channel;
826
cristy49dd6a02011-09-24 23:08:01 +0000827 PixelTrait
828 traits;
829
cristyabace412011-12-11 15:56:53 +0000830 channel=GetPixelChannelMapChannel(image,i);
831 traits=GetPixelChannelMapTraits(image,channel);
cristy49dd6a02011-09-24 23:08:01 +0000832 if (traits == UndefinedPixelTrait)
833 continue;
834 q[i]=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q[i],op,
835 value));
836 }
cristyed231572011-07-14 02:18:59 +0000837 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +0000838 }
839 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
840 status=MagickFalse;
841 if (image->progress_monitor != (MagickProgressMonitor) NULL)
842 {
843 MagickBooleanType
844 proceed;
845
846#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +0000847 #pragma omp critical (MagickCore_EvaluateImage)
cristy351842f2010-03-07 15:27:38 +0000848#endif
849 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
850 if (proceed == MagickFalse)
851 status=MagickFalse;
852 }
853 }
854 image_view=DestroyCacheView(image_view);
855 random_info=DestroyRandomInfoThreadSet(random_info);
856 return(status);
857}
858
859/*
860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861% %
862% %
863% %
864% F u n c t i o n I m a g e %
865% %
866% %
867% %
868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869%
870% FunctionImage() applies a value to the image with an arithmetic, relational,
871% or logical operator to an image. Use these operations to lighten or darken
872% an image, to increase or decrease contrast in an image, or to produce the
873% "negative" of an image.
874%
cristyd42d9952011-07-08 14:21:50 +0000875% The format of the FunctionImage method is:
cristy351842f2010-03-07 15:27:38 +0000876%
877% MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000878% const MagickFunction function,const ssize_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000879% const double *parameters,ExceptionInfo *exception)
cristy351842f2010-03-07 15:27:38 +0000880%
881% A description of each parameter follows:
882%
883% o image: the image.
884%
cristy351842f2010-03-07 15:27:38 +0000885% o function: A channel function.
886%
887% o parameters: one or more parameters.
888%
889% o exception: return any errors or warnings in this structure.
890%
891*/
892
893static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
cristybb503372010-05-27 20:51:26 +0000894 const size_t number_parameters,const double *parameters,
cristy351842f2010-03-07 15:27:38 +0000895 ExceptionInfo *exception)
896{
897 MagickRealType
898 result;
899
cristybb503372010-05-27 20:51:26 +0000900 register ssize_t
cristy351842f2010-03-07 15:27:38 +0000901 i;
902
903 (void) exception;
904 result=0.0;
905 switch (function)
906 {
907 case PolynomialFunction:
908 {
909 /*
cristye2a912b2011-12-05 20:02:07 +0000910 Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+
911 c1*x^2+c2*x+c3).
cristy94a75782011-09-25 02:33:56 +0000912 */
cristy351842f2010-03-07 15:27:38 +0000913 result=0.0;
cristybb503372010-05-27 20:51:26 +0000914 for (i=0; i < (ssize_t) number_parameters; i++)
cristy94a75782011-09-25 02:33:56 +0000915 result=result*QuantumScale*pixel+parameters[i];
916 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000917 break;
918 }
919 case SinusoidFunction:
920 {
cristy94a75782011-09-25 02:33:56 +0000921 MagickRealType
922 amplitude,
923 bias,
924 frequency,
925 phase;
926
927 /*
928 Sinusoid: frequency, phase, amplitude, bias.
929 */
930 frequency=(number_parameters >= 1) ? parameters[0] : 1.0;
931 phase=(number_parameters >= 2) ? parameters[1] : 0.0;
932 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
933 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
934 result=(MagickRealType) (QuantumRange*(amplitude*sin((double) (2.0*
935 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
cristy351842f2010-03-07 15:27:38 +0000936 break;
937 }
938 case ArcsinFunction:
939 {
cristy94a75782011-09-25 02:33:56 +0000940 MagickRealType
941 bias,
942 center,
943 range,
944 width;
945
946 /*
cristye2a912b2011-12-05 20:02:07 +0000947 Arcsin (peged at range limits for invalid results): width, center,
948 range, and bias.
cristy94a75782011-09-25 02:33:56 +0000949 */
950 width=(number_parameters >= 1) ? parameters[0] : 1.0;
951 center=(number_parameters >= 2) ? parameters[1] : 0.5;
952 range=(number_parameters >= 3) ? parameters[2] : 1.0;
953 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
954 result=2.0/width*(QuantumScale*pixel-center);
cristy351842f2010-03-07 15:27:38 +0000955 if ( result <= -1.0 )
cristy94a75782011-09-25 02:33:56 +0000956 result=bias-range/2.0;
cristy351842f2010-03-07 15:27:38 +0000957 else
cristy94a75782011-09-25 02:33:56 +0000958 if (result >= 1.0)
959 result=bias+range/2.0;
960 else
961 result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
962 result*=QuantumRange;
cristy351842f2010-03-07 15:27:38 +0000963 break;
964 }
965 case ArctanFunction:
966 {
cristy94a75782011-09-25 02:33:56 +0000967 MagickRealType
968 center,
969 bias,
970 range,
971 slope;
972
973 /*
974 Arctan: slope, center, range, and bias.
975 */
976 slope=(number_parameters >= 1) ? parameters[0] : 1.0;
977 center=(number_parameters >= 2) ? parameters[1] : 0.5;
978 range=(number_parameters >= 3) ? parameters[2] : 1.0;
979 bias=(number_parameters >= 4) ? parameters[3] : 0.5;
cristy7a07f9f2010-11-27 19:09:51 +0000980 result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
cristy351842f2010-03-07 15:27:38 +0000981 result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
cristy94a75782011-09-25 02:33:56 +0000982 result)+bias));
cristy351842f2010-03-07 15:27:38 +0000983 break;
984 }
985 case UndefinedFunction:
986 break;
987 }
988 return(ClampToQuantum(result));
989}
990
991MagickExport MagickBooleanType FunctionImage(Image *image,
cristybb503372010-05-27 20:51:26 +0000992 const MagickFunction function,const size_t number_parameters,
cristy351842f2010-03-07 15:27:38 +0000993 const double *parameters,ExceptionInfo *exception)
994{
cristy351842f2010-03-07 15:27:38 +0000995#define FunctionImageTag "Function/Image "
996
997 CacheView
998 *image_view;
999
cristy351842f2010-03-07 15:27:38 +00001000 MagickBooleanType
1001 status;
1002
cristy5f959472010-05-27 22:19:46 +00001003 MagickOffsetType
1004 progress;
1005
1006 ssize_t
1007 y;
1008
cristy351842f2010-03-07 15:27:38 +00001009 assert(image != (Image *) NULL);
1010 assert(image->signature == MagickSignature);
1011 if (image->debug != MagickFalse)
1012 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1013 assert(exception != (ExceptionInfo *) NULL);
1014 assert(exception->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +00001015 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1016 return(MagickFalse);
cristy351842f2010-03-07 15:27:38 +00001017 status=MagickTrue;
1018 progress=0;
1019 image_view=AcquireCacheView(image);
1020#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001021 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy351842f2010-03-07 15:27:38 +00001022#endif
cristybb503372010-05-27 20:51:26 +00001023 for (y=0; y < (ssize_t) image->rows; y++)
cristy351842f2010-03-07 15:27:38 +00001024 {
cristy4c08aed2011-07-01 19:47:50 +00001025 register Quantum
cristy351842f2010-03-07 15:27:38 +00001026 *restrict q;
1027
cristy94a75782011-09-25 02:33:56 +00001028 register ssize_t
1029 x;
1030
cristy351842f2010-03-07 15:27:38 +00001031 if (status == MagickFalse)
1032 continue;
1033 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001034 if (q == (Quantum *) NULL)
cristy351842f2010-03-07 15:27:38 +00001035 {
1036 status=MagickFalse;
1037 continue;
1038 }
cristybb503372010-05-27 20:51:26 +00001039 for (x=0; x < (ssize_t) image->columns; x++)
cristy351842f2010-03-07 15:27:38 +00001040 {
cristy94a75782011-09-25 02:33:56 +00001041 register ssize_t
1042 i;
1043
1044 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1045 {
cristyabace412011-12-11 15:56:53 +00001046 PixelChannel
1047 channel;
1048
cristy94a75782011-09-25 02:33:56 +00001049 PixelTrait
1050 traits;
1051
cristyabace412011-12-11 15:56:53 +00001052 channel=GetPixelChannelMapChannel(image,i);
1053 traits=GetPixelChannelMapTraits(image,channel);
cristy94a75782011-09-25 02:33:56 +00001054 if (traits == UndefinedPixelTrait)
1055 continue;
1056 if ((traits & UpdatePixelTrait) == 0)
1057 continue;
1058 q[i]=ApplyFunction(q[i],function,number_parameters,parameters,
1059 exception);
1060 }
cristyed231572011-07-14 02:18:59 +00001061 q+=GetPixelChannels(image);
cristy351842f2010-03-07 15:27:38 +00001062 }
1063 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1064 status=MagickFalse;
1065 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1066 {
1067 MagickBooleanType
1068 proceed;
1069
1070#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd42d9952011-07-08 14:21:50 +00001071 #pragma omp critical (MagickCore_FunctionImage)
cristy351842f2010-03-07 15:27:38 +00001072#endif
1073 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
1074 if (proceed == MagickFalse)
1075 status=MagickFalse;
1076 }
1077 }
1078 image_view=DestroyCacheView(image_view);
1079 return(status);
1080}
1081
1082/*
1083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084% %
1085% %
1086% %
cristyd42d9952011-07-08 14:21:50 +00001087% G e t I m a g e E x t r e m a %
cristy3ed852e2009-09-05 21:47:34 +00001088% %
1089% %
1090% %
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092%
cristyd42d9952011-07-08 14:21:50 +00001093% GetImageExtrema() returns the extrema of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001094%
cristyd42d9952011-07-08 14:21:50 +00001095% The format of the GetImageExtrema method is:
cristy3ed852e2009-09-05 21:47:34 +00001096%
cristyd42d9952011-07-08 14:21:50 +00001097% MagickBooleanType GetImageExtrema(const Image *image,size_t *minima,
1098% size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001099%
1100% A description of each parameter follows:
1101%
1102% o image: the image.
1103%
cristy3ed852e2009-09-05 21:47:34 +00001104% o minima: the minimum value in the channel.
1105%
1106% o maxima: the maximum value in the channel.
1107%
1108% o exception: return any errors or warnings in this structure.
1109%
1110*/
cristy3ed852e2009-09-05 21:47:34 +00001111MagickExport MagickBooleanType GetImageExtrema(const Image *image,
cristybb503372010-05-27 20:51:26 +00001112 size_t *minima,size_t *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001113{
cristy3ed852e2009-09-05 21:47:34 +00001114 double
1115 max,
1116 min;
1117
1118 MagickBooleanType
1119 status;
1120
1121 assert(image != (Image *) NULL);
1122 assert(image->signature == MagickSignature);
1123 if (image->debug != MagickFalse)
1124 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001125 status=GetImageRange(image,&min,&max,exception);
cristybb503372010-05-27 20:51:26 +00001126 *minima=(size_t) ceil(min-0.5);
1127 *maxima=(size_t) floor(max+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001128 return(status);
1129}
1130
1131/*
1132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1133% %
1134% %
1135% %
cristyd42d9952011-07-08 14:21:50 +00001136% G e t I m a g e M e a n %
cristy3ed852e2009-09-05 21:47:34 +00001137% %
1138% %
1139% %
1140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1141%
cristyd42d9952011-07-08 14:21:50 +00001142% GetImageMean() returns the mean and standard deviation of one or more
cristy3ed852e2009-09-05 21:47:34 +00001143% image channels.
1144%
cristyd42d9952011-07-08 14:21:50 +00001145% The format of the GetImageMean method is:
cristy3ed852e2009-09-05 21:47:34 +00001146%
cristyd42d9952011-07-08 14:21:50 +00001147% MagickBooleanType GetImageMean(const Image *image,double *mean,
1148% double *standard_deviation,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001149%
1150% A description of each parameter follows:
1151%
1152% o image: the image.
1153%
cristy3ed852e2009-09-05 21:47:34 +00001154% o mean: the average value in the channel.
1155%
1156% o standard_deviation: the standard deviation of the channel.
1157%
1158% o exception: return any errors or warnings in this structure.
1159%
1160*/
cristy3ed852e2009-09-05 21:47:34 +00001161MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
1162 double *standard_deviation,ExceptionInfo *exception)
1163{
cristyfd9dcd42010-08-08 18:07:02 +00001164 ChannelStatistics
1165 *channel_statistics;
cristy3ed852e2009-09-05 21:47:34 +00001166
cristy94a75782011-09-25 02:33:56 +00001167 register ssize_t
1168 i;
1169
cristyfd9dcd42010-08-08 18:07:02 +00001170 size_t
cristy94a75782011-09-25 02:33:56 +00001171 area;
cristy3ed852e2009-09-05 21:47:34 +00001172
1173 assert(image != (Image *) NULL);
1174 assert(image->signature == MagickSignature);
1175 if (image->debug != MagickFalse)
1176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristyd42d9952011-07-08 14:21:50 +00001177 channel_statistics=GetImageStatistics(image,exception);
cristyfd9dcd42010-08-08 18:07:02 +00001178 if (channel_statistics == (ChannelStatistics *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001179 return(MagickFalse);
cristy94a75782011-09-25 02:33:56 +00001180 area=0;
cristy5f95f4f2011-10-23 01:01:01 +00001181 channel_statistics[CompositePixelChannel].mean=0.0;
1182 channel_statistics[CompositePixelChannel].standard_deviation=0.0;
cristy94a75782011-09-25 02:33:56 +00001183 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1184 {
cristyabace412011-12-11 15:56:53 +00001185 PixelChannel
1186 channel;
1187
cristy94a75782011-09-25 02:33:56 +00001188 PixelTrait
1189 traits;
1190
cristyabace412011-12-11 15:56:53 +00001191 channel=GetPixelChannelMapChannel(image,i);
1192 traits=GetPixelChannelMapTraits(image,channel);
cristy94a75782011-09-25 02:33:56 +00001193 if (traits == UndefinedPixelTrait)
1194 continue;
1195 if ((traits & UpdatePixelTrait) == 0)
1196 continue;
cristy5f95f4f2011-10-23 01:01:01 +00001197 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
1198 channel_statistics[CompositePixelChannel].standard_deviation+=
cristy94a75782011-09-25 02:33:56 +00001199 channel_statistics[i].variance-channel_statistics[i].mean*
1200 channel_statistics[i].mean;
1201 area++;
1202 }
cristy5f95f4f2011-10-23 01:01:01 +00001203 channel_statistics[CompositePixelChannel].mean/=area;
1204 channel_statistics[CompositePixelChannel].standard_deviation=
1205 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area);
1206 *mean=channel_statistics[CompositePixelChannel].mean;
cristye2a912b2011-12-05 20:02:07 +00001207 *standard_deviation=
1208 channel_statistics[CompositePixelChannel].standard_deviation;
cristyfd9dcd42010-08-08 18:07:02 +00001209 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1210 channel_statistics);
1211 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001212}
1213
1214/*
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216% %
1217% %
1218% %
cristyd42d9952011-07-08 14:21:50 +00001219% G e t I m a g e K u r t o s i s %
cristy3ed852e2009-09-05 21:47:34 +00001220% %
1221% %
1222% %
1223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1224%
cristyd42d9952011-07-08 14:21:50 +00001225% GetImageKurtosis() returns the kurtosis and skewness of one or more
cristy3ed852e2009-09-05 21:47:34 +00001226% image channels.
1227%
cristyd42d9952011-07-08 14:21:50 +00001228% The format of the GetImageKurtosis method is:
cristy3ed852e2009-09-05 21:47:34 +00001229%
cristyd42d9952011-07-08 14:21:50 +00001230% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
1231% double *skewness,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001232%
1233% A description of each parameter follows:
1234%
1235% o image: the image.
1236%
cristy3ed852e2009-09-05 21:47:34 +00001237% o kurtosis: the kurtosis of the channel.
1238%
1239% o skewness: the skewness of the channel.
1240%
1241% o exception: return any errors or warnings in this structure.
1242%
1243*/
cristy3ed852e2009-09-05 21:47:34 +00001244MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
1245 double *kurtosis,double *skewness,ExceptionInfo *exception)
1246{
cristy94a75782011-09-25 02:33:56 +00001247 CacheView
1248 *image_view;
1249
cristy3ed852e2009-09-05 21:47:34 +00001250 double
1251 area,
1252 mean,
1253 standard_deviation,
1254 sum_squares,
1255 sum_cubes,
1256 sum_fourth_power;
1257
cristy94a75782011-09-25 02:33:56 +00001258 MagickBooleanType
1259 status;
1260
cristybb503372010-05-27 20:51:26 +00001261 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001262 y;
1263
1264 assert(image != (Image *) NULL);
1265 assert(image->signature == MagickSignature);
1266 if (image->debug != MagickFalse)
1267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy94a75782011-09-25 02:33:56 +00001268 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001269 *kurtosis=0.0;
1270 *skewness=0.0;
1271 area=0.0;
1272 mean=0.0;
1273 standard_deviation=0.0;
1274 sum_squares=0.0;
1275 sum_cubes=0.0;
1276 sum_fourth_power=0.0;
cristy94a75782011-09-25 02:33:56 +00001277 image_view=AcquireCacheView(image);
1278#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001279 #pragma omp parallel for schedule(static) shared(status) omp_throttle(1)
cristy94a75782011-09-25 02:33:56 +00001280#endif
cristybb503372010-05-27 20:51:26 +00001281 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001282 {
cristy4c08aed2011-07-01 19:47:50 +00001283 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001284 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001285
cristybb503372010-05-27 20:51:26 +00001286 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001287 x;
1288
cristy94a75782011-09-25 02:33:56 +00001289 if (status == MagickFalse)
1290 continue;
1291 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001292 if (p == (const Quantum *) NULL)
cristy94a75782011-09-25 02:33:56 +00001293 {
1294 status=MagickFalse;
1295 continue;
1296 }
cristybb503372010-05-27 20:51:26 +00001297 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001298 {
cristy94a75782011-09-25 02:33:56 +00001299 register ssize_t
1300 i;
1301
1302 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1303 {
cristyabace412011-12-11 15:56:53 +00001304 PixelChannel
1305 channel;
1306
cristy94a75782011-09-25 02:33:56 +00001307 PixelTrait
1308 traits;
1309
cristyabace412011-12-11 15:56:53 +00001310 channel=GetPixelChannelMapChannel(image,i);
1311 traits=GetPixelChannelMapTraits(image,channel);
cristy94a75782011-09-25 02:33:56 +00001312 if (traits == UndefinedPixelTrait)
1313 continue;
1314 if ((traits & UpdatePixelTrait) == 0)
1315 continue;
1316#if defined(MAGICKCORE_OPENMP_SUPPORT)
1317 #pragma omp critical (MagickCore_GetImageKurtosis)
1318#endif
cristy3ed852e2009-09-05 21:47:34 +00001319 {
cristy94a75782011-09-25 02:33:56 +00001320 mean+=p[i];
1321 sum_squares+=(double) p[i]*p[i];
1322 sum_cubes+=(double) p[i]*p[i]*p[i];
1323 sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
cristy3ed852e2009-09-05 21:47:34 +00001324 area++;
1325 }
cristy94a75782011-09-25 02:33:56 +00001326 }
cristyed231572011-07-14 02:18:59 +00001327 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001328 }
1329 }
cristy94a75782011-09-25 02:33:56 +00001330 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001331 if (area != 0.0)
1332 {
1333 mean/=area;
1334 sum_squares/=area;
1335 sum_cubes/=area;
1336 sum_fourth_power/=area;
1337 }
1338 standard_deviation=sqrt(sum_squares-(mean*mean));
1339 if (standard_deviation != 0.0)
1340 {
1341 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
1342 3.0*mean*mean*mean*mean;
1343 *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
1344 standard_deviation;
1345 *kurtosis-=3.0;
1346 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
1347 *skewness/=standard_deviation*standard_deviation*standard_deviation;
1348 }
cristy94a75782011-09-25 02:33:56 +00001349 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001350}
cristy46f08202010-01-10 04:04:21 +00001351
cristy3ed852e2009-09-05 21:47:34 +00001352/*
1353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354% %
1355% %
1356% %
cristyd42d9952011-07-08 14:21:50 +00001357% G e t I m a g e R a n g e %
cristy3ed852e2009-09-05 21:47:34 +00001358% %
1359% %
1360% %
1361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1362%
cristyd42d9952011-07-08 14:21:50 +00001363% GetImageRange() returns the range of one or more image channels.
cristy3ed852e2009-09-05 21:47:34 +00001364%
cristyd42d9952011-07-08 14:21:50 +00001365% The format of the GetImageRange method is:
cristy3ed852e2009-09-05 21:47:34 +00001366%
cristyd42d9952011-07-08 14:21:50 +00001367% MagickBooleanType GetImageRange(const Image *image,double *minima,
1368% double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001369%
1370% A description of each parameter follows:
1371%
1372% o image: the image.
1373%
cristy3ed852e2009-09-05 21:47:34 +00001374% o minima: the minimum value in the channel.
1375%
1376% o maxima: the maximum value in the channel.
1377%
1378% o exception: return any errors or warnings in this structure.
1379%
1380*/
cristyd42d9952011-07-08 14:21:50 +00001381MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
1382 double *maxima,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001383{
cristy4a9be682011-09-25 02:02:32 +00001384 CacheView
1385 *image_view;
1386
1387 MagickBooleanType
1388 status;
cristy3ed852e2009-09-05 21:47:34 +00001389
cristy9d314ff2011-03-09 01:30:28 +00001390 ssize_t
1391 y;
1392
cristy3ed852e2009-09-05 21:47:34 +00001393 assert(image != (Image *) NULL);
1394 assert(image->signature == MagickSignature);
1395 if (image->debug != MagickFalse)
1396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy4a9be682011-09-25 02:02:32 +00001397 status=MagickTrue;
cristyaa8634f2011-10-01 13:25:12 +00001398 *maxima=(-MagickHuge);
1399 *minima=MagickHuge;
cristy4a9be682011-09-25 02:02:32 +00001400 image_view=AcquireCacheView(image);
1401#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001402 #pragma omp parallel for schedule(static) shared(status) omp_throttle(1)
cristy4a9be682011-09-25 02:02:32 +00001403#endif
cristybb503372010-05-27 20:51:26 +00001404 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001405 {
cristy4c08aed2011-07-01 19:47:50 +00001406 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001407 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001408
cristybb503372010-05-27 20:51:26 +00001409 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001410 x;
1411
cristy4a9be682011-09-25 02:02:32 +00001412 if (status == MagickFalse)
1413 continue;
1414 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001415 if (p == (const Quantum *) NULL)
cristy4a9be682011-09-25 02:02:32 +00001416 {
1417 status=MagickFalse;
1418 continue;
1419 }
cristybb503372010-05-27 20:51:26 +00001420 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001421 {
cristy4a9be682011-09-25 02:02:32 +00001422 register ssize_t
1423 i;
1424
1425 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1426 {
cristyabace412011-12-11 15:56:53 +00001427 PixelChannel
1428 channel;
1429
cristy4a9be682011-09-25 02:02:32 +00001430 PixelTrait
1431 traits;
1432
cristyabace412011-12-11 15:56:53 +00001433 channel=GetPixelChannelMapChannel(image,i);
1434 traits=GetPixelChannelMapTraits(image,channel);
cristy4a9be682011-09-25 02:02:32 +00001435 if (traits == UndefinedPixelTrait)
1436 continue;
1437 if ((traits & UpdatePixelTrait) == 0)
1438 continue;
1439#if defined(MAGICKCORE_OPENMP_SUPPORT)
1440 #pragma omp critical (MagickCore_GetImageRange)
1441#endif
cristy3ed852e2009-09-05 21:47:34 +00001442 {
cristyba18a7a2011-09-25 15:43:43 +00001443 if (p[i] < *minima)
cristy4a9be682011-09-25 02:02:32 +00001444 *minima=(double) p[i];
cristyba18a7a2011-09-25 15:43:43 +00001445 if (p[i] > *maxima)
cristy4a9be682011-09-25 02:02:32 +00001446 *maxima=(double) p[i];
cristy3ed852e2009-09-05 21:47:34 +00001447 }
cristy4a9be682011-09-25 02:02:32 +00001448 }
cristyed231572011-07-14 02:18:59 +00001449 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001450 }
1451 }
cristy4a9be682011-09-25 02:02:32 +00001452 image_view=DestroyCacheView(image_view);
1453 return(status);
cristy3ed852e2009-09-05 21:47:34 +00001454}
1455
1456/*
1457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458% %
1459% %
1460% %
cristyd42d9952011-07-08 14:21:50 +00001461% G e t I m a g e S t a t i s t i c s %
cristy3ed852e2009-09-05 21:47:34 +00001462% %
1463% %
1464% %
1465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466%
cristyd42d9952011-07-08 14:21:50 +00001467% GetImageStatistics() returns statistics for each channel in the
cristy3ed852e2009-09-05 21:47:34 +00001468% image. The statistics include the channel depth, its minima, maxima, mean,
1469% standard deviation, kurtosis and skewness. You can access the red channel
1470% mean, for example, like this:
1471%
cristyd42d9952011-07-08 14:21:50 +00001472% channel_statistics=GetImageStatistics(image,exception);
cristy49dd6a02011-09-24 23:08:01 +00001473% red_mean=channel_statistics[RedPixelChannel].mean;
cristy3ed852e2009-09-05 21:47:34 +00001474%
1475% Use MagickRelinquishMemory() to free the statistics buffer.
1476%
cristyd42d9952011-07-08 14:21:50 +00001477% The format of the GetImageStatistics method is:
cristy3ed852e2009-09-05 21:47:34 +00001478%
cristyd42d9952011-07-08 14:21:50 +00001479% ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001480% ExceptionInfo *exception)
1481%
1482% A description of each parameter follows:
1483%
1484% o image: the image.
1485%
1486% o exception: return any errors or warnings in this structure.
1487%
1488*/
cristy49dd6a02011-09-24 23:08:01 +00001489
1490static size_t GetImageChannels(const Image *image)
1491{
1492 register ssize_t
1493 i;
1494
1495 size_t
1496 channels;
1497
1498 channels=0;
1499 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1500 {
cristyabace412011-12-11 15:56:53 +00001501 PixelChannel
1502 channel;
1503
cristy49dd6a02011-09-24 23:08:01 +00001504 PixelTrait
1505 traits;
1506
cristyabace412011-12-11 15:56:53 +00001507 channel=GetPixelChannelMapChannel(image,i);
1508 traits=GetPixelChannelMapTraits(image,channel);
cristy49dd6a02011-09-24 23:08:01 +00001509 if ((traits & UpdatePixelTrait) != 0)
1510 channels++;
1511 }
1512 return(channels);
1513}
1514
cristyd42d9952011-07-08 14:21:50 +00001515MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001516 ExceptionInfo *exception)
1517{
1518 ChannelStatistics
1519 *channel_statistics;
1520
1521 double
cristyfd9dcd42010-08-08 18:07:02 +00001522 area;
cristy3ed852e2009-09-05 21:47:34 +00001523
cristy3ed852e2009-09-05 21:47:34 +00001524 MagickStatusType
1525 status;
1526
1527 QuantumAny
1528 range;
1529
cristybb503372010-05-27 20:51:26 +00001530 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001531 i;
1532
1533 size_t
cristy9d314ff2011-03-09 01:30:28 +00001534 channels,
cristy49dd6a02011-09-24 23:08:01 +00001535 depth;
cristy3ed852e2009-09-05 21:47:34 +00001536
cristy9d314ff2011-03-09 01:30:28 +00001537 ssize_t
1538 y;
cristy3ed852e2009-09-05 21:47:34 +00001539
1540 assert(image != (Image *) NULL);
1541 assert(image->signature == MagickSignature);
1542 if (image->debug != MagickFalse)
1543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy49dd6a02011-09-24 23:08:01 +00001544 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
1545 MaxPixelChannels+1,sizeof(*channel_statistics));
cristy3ed852e2009-09-05 21:47:34 +00001546 if (channel_statistics == (ChannelStatistics *) NULL)
1547 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy49dd6a02011-09-24 23:08:01 +00001548 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
cristy3ed852e2009-09-05 21:47:34 +00001549 sizeof(*channel_statistics));
cristy25a5f2f2011-09-24 14:09:43 +00001550 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001551 {
1552 channel_statistics[i].depth=1;
cristyaa8634f2011-10-01 13:25:12 +00001553 channel_statistics[i].maxima=(-MagickHuge);
1554 channel_statistics[i].minima=MagickHuge;
cristy3ed852e2009-09-05 21:47:34 +00001555 }
cristybb503372010-05-27 20:51:26 +00001556 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001557 {
cristy4c08aed2011-07-01 19:47:50 +00001558 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001559 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001560
cristybb503372010-05-27 20:51:26 +00001561 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001562 x;
1563
1564 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001565 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001566 break;
cristy49dd6a02011-09-24 23:08:01 +00001567 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001568 {
cristy49dd6a02011-09-24 23:08:01 +00001569 register ssize_t
1570 i;
1571
1572 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1573 {
cristyabace412011-12-11 15:56:53 +00001574 PixelChannel
1575 channel;
1576
cristy49dd6a02011-09-24 23:08:01 +00001577 PixelTrait
1578 traits;
1579
cristyabace412011-12-11 15:56:53 +00001580 channel=GetPixelChannelMapChannel(image,i);
1581 traits=GetPixelChannelMapTraits(image,channel);
cristy49dd6a02011-09-24 23:08:01 +00001582 if (traits == UndefinedPixelTrait)
1583 continue;
1584 if (channel_statistics[i].depth != MAGICKCORE_QUANTUM_DEPTH)
1585 {
1586 depth=channel_statistics[i].depth;
1587 range=GetQuantumRange(depth);
1588 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),
1589 range) ? MagickTrue : MagickFalse;
1590 if (status != MagickFalse)
1591 {
cristycd8ce3e2011-12-14 12:33:23 +00001592 channel_statistics[channel].depth++;
cristy49dd6a02011-09-24 23:08:01 +00001593 continue;
1594 }
cristy3ed852e2009-09-05 21:47:34 +00001595 }
cristycd8ce3e2011-12-14 12:33:23 +00001596 if ((double) p[i] < channel_statistics[channel].minima)
1597 channel_statistics[channel].minima=(double) p[i];
1598 if ((double) p[i] > channel_statistics[channel].maxima)
1599 channel_statistics[channel].maxima=(double) p[i];
1600 channel_statistics[channel].sum+=p[i];
1601 channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
1602 channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
1603 channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
1604 p[i];
cristy49dd6a02011-09-24 23:08:01 +00001605 }
cristyed231572011-07-14 02:18:59 +00001606 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001607 }
1608 }
1609 area=(double) image->columns*image->rows;
cristy25a5f2f2011-09-24 14:09:43 +00001610 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001611 {
cristyfd9dcd42010-08-08 18:07:02 +00001612 channel_statistics[i].sum/=area;
1613 channel_statistics[i].sum_squared/=area;
cristya8178ed2010-08-10 17:31:59 +00001614 channel_statistics[i].sum_cubed/=area;
1615 channel_statistics[i].sum_fourth_power/=area;
cristyfd9dcd42010-08-08 18:07:02 +00001616 channel_statistics[i].mean=channel_statistics[i].sum;
1617 channel_statistics[i].variance=channel_statistics[i].sum_squared;
1618 channel_statistics[i].standard_deviation=sqrt(
1619 channel_statistics[i].variance-(channel_statistics[i].mean*
1620 channel_statistics[i].mean));
cristy3ed852e2009-09-05 21:47:34 +00001621 }
cristy25a5f2f2011-09-24 14:09:43 +00001622 for (i=0; i < (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001623 {
cristy99bd5232011-12-07 14:38:20 +00001624 channel_statistics[CompositePixelChannel].depth=(size_t) EvaluateMax(
1625 (double) channel_statistics[CompositePixelChannel].depth,(double)
cristy3ed852e2009-09-05 21:47:34 +00001626 channel_statistics[i].depth);
cristy5f95f4f2011-10-23 01:01:01 +00001627 channel_statistics[CompositePixelChannel].minima=MagickMin(
1628 channel_statistics[CompositePixelChannel].minima,
cristy32daba42011-05-01 02:34:39 +00001629 channel_statistics[i].minima);
cristy99bd5232011-12-07 14:38:20 +00001630 channel_statistics[CompositePixelChannel].maxima=EvaluateMax(
cristy5f95f4f2011-10-23 01:01:01 +00001631 channel_statistics[CompositePixelChannel].maxima,
cristy32daba42011-05-01 02:34:39 +00001632 channel_statistics[i].maxima);
cristy5f95f4f2011-10-23 01:01:01 +00001633 channel_statistics[CompositePixelChannel].sum+=channel_statistics[i].sum;
1634 channel_statistics[CompositePixelChannel].sum_squared+=
cristyfd9dcd42010-08-08 18:07:02 +00001635 channel_statistics[i].sum_squared;
cristy5f95f4f2011-10-23 01:01:01 +00001636 channel_statistics[CompositePixelChannel].sum_cubed+=
cristy32daba42011-05-01 02:34:39 +00001637 channel_statistics[i].sum_cubed;
cristy5f95f4f2011-10-23 01:01:01 +00001638 channel_statistics[CompositePixelChannel].sum_fourth_power+=
cristya8178ed2010-08-10 17:31:59 +00001639 channel_statistics[i].sum_fourth_power;
cristy5f95f4f2011-10-23 01:01:01 +00001640 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean;
1641 channel_statistics[CompositePixelChannel].variance+=
cristy32daba42011-05-01 02:34:39 +00001642 channel_statistics[i].variance-channel_statistics[i].mean*
1643 channel_statistics[i].mean;
cristy5f95f4f2011-10-23 01:01:01 +00001644 channel_statistics[CompositePixelChannel].standard_deviation+=
cristyfd9dcd42010-08-08 18:07:02 +00001645 channel_statistics[i].variance-channel_statistics[i].mean*
1646 channel_statistics[i].mean;
cristy3ed852e2009-09-05 21:47:34 +00001647 }
cristy49dd6a02011-09-24 23:08:01 +00001648 channels=GetImageChannels(image);
cristy5f95f4f2011-10-23 01:01:01 +00001649 channel_statistics[CompositePixelChannel].sum/=channels;
1650 channel_statistics[CompositePixelChannel].sum_squared/=channels;
1651 channel_statistics[CompositePixelChannel].sum_cubed/=channels;
1652 channel_statistics[CompositePixelChannel].sum_fourth_power/=channels;
1653 channel_statistics[CompositePixelChannel].mean/=channels;
1654 channel_statistics[CompositePixelChannel].variance/=channels;
1655 channel_statistics[CompositePixelChannel].standard_deviation=
1656 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/channels);
1657 channel_statistics[CompositePixelChannel].kurtosis/=channels;
1658 channel_statistics[CompositePixelChannel].skewness/=channels;
cristy25a5f2f2011-09-24 14:09:43 +00001659 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001660 {
cristy3ed852e2009-09-05 21:47:34 +00001661 if (channel_statistics[i].standard_deviation == 0.0)
cristya8178ed2010-08-10 17:31:59 +00001662 continue;
cristye2a912b2011-12-05 20:02:07 +00001663 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
1664 channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
1665 channel_statistics[i].mean*channel_statistics[i].mean*
cristya8178ed2010-08-10 17:31:59 +00001666 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1667 channel_statistics[i].standard_deviation*
1668 channel_statistics[i].standard_deviation);
cristye2a912b2011-12-05 20:02:07 +00001669 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
1670 channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
1671 channel_statistics[i].mean*channel_statistics[i].mean*
cristya8178ed2010-08-10 17:31:59 +00001672 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
1673 channel_statistics[i].mean*1.0*channel_statistics[i].mean*
1674 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
1675 channel_statistics[i].standard_deviation*
1676 channel_statistics[i].standard_deviation*
1677 channel_statistics[i].standard_deviation)-3.0;
cristy3ed852e2009-09-05 21:47:34 +00001678 }
1679 return(channel_statistics);
1680}
cristy99bd5232011-12-07 14:38:20 +00001681
1682/*
1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684% %
1685% %
1686% %
1687% S t a t i s t i c I m a g e %
1688% %
1689% %
1690% %
1691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1692%
1693% StatisticImage() makes each pixel the min / max / median / mode / etc. of
1694% the neighborhood of the specified width and height.
1695%
1696% The format of the StatisticImage method is:
1697%
1698% Image *StatisticImage(const Image *image,const StatisticType type,
1699% const size_t width,const size_t height,ExceptionInfo *exception)
1700%
1701% A description of each parameter follows:
1702%
1703% o image: the image.
1704%
1705% o type: the statistic type (median, mode, etc.).
1706%
1707% o width: the width of the pixel neighborhood.
1708%
1709% o height: the height of the pixel neighborhood.
1710%
1711% o exception: return any errors or warnings in this structure.
1712%
1713*/
1714
1715typedef struct _SkipNode
1716{
1717 size_t
1718 next[9],
1719 count,
1720 signature;
1721} SkipNode;
1722
1723typedef struct _SkipList
1724{
1725 ssize_t
1726 level;
1727
1728 SkipNode
1729 *nodes;
1730} SkipList;
1731
1732typedef struct _PixelList
1733{
1734 size_t
1735 length,
1736 seed;
1737
1738 SkipList
1739 skip_list;
1740
1741 size_t
1742 signature;
1743} PixelList;
1744
1745static PixelList *DestroyPixelList(PixelList *pixel_list)
1746{
1747 if (pixel_list == (PixelList *) NULL)
1748 return((PixelList *) NULL);
1749 if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
1750 pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
1751 pixel_list->skip_list.nodes);
1752 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
1753 return(pixel_list);
1754}
1755
1756static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
1757{
1758 register ssize_t
1759 i;
1760
1761 assert(pixel_list != (PixelList **) NULL);
1762 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1763 if (pixel_list[i] != (PixelList *) NULL)
1764 pixel_list[i]=DestroyPixelList(pixel_list[i]);
1765 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
1766 return(pixel_list);
1767}
1768
1769static PixelList *AcquirePixelList(const size_t width,const size_t height)
1770{
1771 PixelList
1772 *pixel_list;
1773
1774 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
1775 if (pixel_list == (PixelList *) NULL)
1776 return(pixel_list);
1777 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
1778 pixel_list->length=width*height;
1779 pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
1780 sizeof(*pixel_list->skip_list.nodes));
1781 if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
1782 return(DestroyPixelList(pixel_list));
1783 (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
1784 sizeof(*pixel_list->skip_list.nodes));
1785 pixel_list->signature=MagickSignature;
1786 return(pixel_list);
1787}
1788
1789static PixelList **AcquirePixelListThreadSet(const size_t width,
1790 const size_t height)
1791{
1792 PixelList
1793 **pixel_list;
1794
1795 register ssize_t
1796 i;
1797
1798 size_t
1799 number_threads;
1800
1801 number_threads=GetOpenMPMaximumThreads();
1802 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
1803 sizeof(*pixel_list));
1804 if (pixel_list == (PixelList **) NULL)
1805 return((PixelList **) NULL);
1806 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
1807 for (i=0; i < (ssize_t) number_threads; i++)
1808 {
1809 pixel_list[i]=AcquirePixelList(width,height);
1810 if (pixel_list[i] == (PixelList *) NULL)
1811 return(DestroyPixelListThreadSet(pixel_list));
1812 }
1813 return(pixel_list);
1814}
1815
1816static void AddNodePixelList(PixelList *pixel_list,const size_t color)
1817{
1818 register SkipList
1819 *p;
1820
1821 register ssize_t
1822 level;
1823
1824 size_t
1825 search,
1826 update[9];
1827
1828 /*
1829 Initialize the node.
1830 */
1831 p=(&pixel_list->skip_list);
1832 p->nodes[color].signature=pixel_list->signature;
1833 p->nodes[color].count=1;
1834 /*
1835 Determine where it belongs in the list.
1836 */
1837 search=65536UL;
1838 for (level=p->level; level >= 0; level--)
1839 {
1840 while (p->nodes[search].next[level] < color)
1841 search=p->nodes[search].next[level];
1842 update[level]=search;
1843 }
1844 /*
1845 Generate a pseudo-random level for this node.
1846 */
1847 for (level=0; ; level++)
1848 {
1849 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
1850 if ((pixel_list->seed & 0x300) != 0x300)
1851 break;
1852 }
1853 if (level > 8)
1854 level=8;
1855 if (level > (p->level+2))
1856 level=p->level+2;
1857 /*
1858 If we're raising the list's level, link back to the root node.
1859 */
1860 while (level > p->level)
1861 {
1862 p->level++;
1863 update[p->level]=65536UL;
1864 }
1865 /*
1866 Link the node into the skip-list.
1867 */
1868 do
1869 {
1870 p->nodes[color].next[level]=p->nodes[update[level]].next[level];
1871 p->nodes[update[level]].next[level]=color;
1872 } while (level-- > 0);
1873}
1874
1875static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel)
1876{
1877 register SkipList
1878 *p;
1879
1880 size_t
1881 color,
1882 maximum;
1883
1884 ssize_t
1885 count;
1886
1887 /*
1888 Find the maximum value for each of the color.
1889 */
1890 p=(&pixel_list->skip_list);
1891 color=65536L;
1892 count=0;
1893 maximum=p->nodes[color].next[0];
1894 do
1895 {
1896 color=p->nodes[color].next[0];
1897 if (color > maximum)
1898 maximum=color;
1899 count+=p->nodes[color].count;
1900 } while (count < (ssize_t) pixel_list->length);
1901 *pixel=ScaleShortToQuantum((unsigned short) maximum);
1902}
1903
1904static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
1905{
1906 MagickRealType
1907 sum;
1908
1909 register SkipList
1910 *p;
1911
1912 size_t
1913 color;
1914
1915 ssize_t
1916 count;
1917
1918 /*
1919 Find the mean value for each of the color.
1920 */
1921 p=(&pixel_list->skip_list);
1922 color=65536L;
1923 count=0;
1924 sum=0.0;
1925 do
1926 {
1927 color=p->nodes[color].next[0];
1928 sum+=(MagickRealType) p->nodes[color].count*color;
1929 count+=p->nodes[color].count;
1930 } while (count < (ssize_t) pixel_list->length);
1931 sum/=pixel_list->length;
1932 *pixel=ScaleShortToQuantum((unsigned short) sum);
1933}
1934
1935static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel)
1936{
1937 register SkipList
1938 *p;
1939
1940 size_t
1941 color;
1942
1943 ssize_t
1944 count;
1945
1946 /*
1947 Find the median value for each of the color.
1948 */
1949 p=(&pixel_list->skip_list);
1950 color=65536L;
1951 count=0;
1952 do
1953 {
1954 color=p->nodes[color].next[0];
1955 count+=p->nodes[color].count;
1956 } while (count <= (ssize_t) (pixel_list->length >> 1));
1957 *pixel=ScaleShortToQuantum((unsigned short) color);
1958}
1959
1960static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel)
1961{
1962 register SkipList
1963 *p;
1964
1965 size_t
1966 color,
1967 minimum;
1968
1969 ssize_t
1970 count;
1971
1972 /*
1973 Find the minimum value for each of the color.
1974 */
1975 p=(&pixel_list->skip_list);
1976 count=0;
1977 color=65536UL;
1978 minimum=p->nodes[color].next[0];
1979 do
1980 {
1981 color=p->nodes[color].next[0];
1982 if (color < minimum)
1983 minimum=color;
1984 count+=p->nodes[color].count;
1985 } while (count < (ssize_t) pixel_list->length);
1986 *pixel=ScaleShortToQuantum((unsigned short) minimum);
1987}
1988
1989static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel)
1990{
1991 register SkipList
1992 *p;
1993
1994 size_t
1995 color,
1996 max_count,
1997 mode;
1998
1999 ssize_t
2000 count;
2001
2002 /*
2003 Make each pixel the 'predominant color' of the specified neighborhood.
2004 */
2005 p=(&pixel_list->skip_list);
2006 color=65536L;
2007 mode=color;
2008 max_count=p->nodes[mode].count;
2009 count=0;
2010 do
2011 {
2012 color=p->nodes[color].next[0];
2013 if (p->nodes[color].count > max_count)
2014 {
2015 mode=color;
2016 max_count=p->nodes[mode].count;
2017 }
2018 count+=p->nodes[color].count;
2019 } while (count < (ssize_t) pixel_list->length);
2020 *pixel=ScaleShortToQuantum((unsigned short) mode);
2021}
2022
2023static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel)
2024{
2025 register SkipList
2026 *p;
2027
2028 size_t
2029 color,
2030 next,
2031 previous;
2032
2033 ssize_t
2034 count;
2035
2036 /*
2037 Finds the non peak value for each of the colors.
2038 */
2039 p=(&pixel_list->skip_list);
2040 color=65536L;
2041 next=p->nodes[color].next[0];
2042 count=0;
2043 do
2044 {
2045 previous=color;
2046 color=next;
2047 next=p->nodes[color].next[0];
2048 count+=p->nodes[color].count;
2049 } while (count <= (ssize_t) (pixel_list->length >> 1));
2050 if ((previous == 65536UL) && (next != 65536UL))
2051 color=next;
2052 else
2053 if ((previous != 65536UL) && (next == 65536UL))
2054 color=previous;
2055 *pixel=ScaleShortToQuantum((unsigned short) color);
2056}
2057
2058static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
2059 Quantum *pixel)
2060{
2061 MagickRealType
2062 sum,
2063 sum_squared;
2064
2065 register SkipList
2066 *p;
2067
2068 size_t
2069 color;
2070
2071 ssize_t
2072 count;
2073
2074 /*
2075 Find the standard-deviation value for each of the color.
2076 */
2077 p=(&pixel_list->skip_list);
2078 color=65536L;
2079 count=0;
2080 sum=0.0;
2081 sum_squared=0.0;
2082 do
2083 {
2084 register ssize_t
2085 i;
2086
2087 color=p->nodes[color].next[0];
2088 sum+=(MagickRealType) p->nodes[color].count*color;
2089 for (i=0; i < (ssize_t) p->nodes[color].count; i++)
2090 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
2091 count+=p->nodes[color].count;
2092 } while (count < (ssize_t) pixel_list->length);
2093 sum/=pixel_list->length;
2094 sum_squared/=pixel_list->length;
2095 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
2096}
2097
2098static inline void InsertPixelList(const Image *image,const Quantum pixel,
2099 PixelList *pixel_list)
2100{
2101 size_t
2102 signature;
2103
2104 unsigned short
2105 index;
2106
2107 index=ScaleQuantumToShort(pixel);
2108 signature=pixel_list->skip_list.nodes[index].signature;
2109 if (signature == pixel_list->signature)
2110 {
2111 pixel_list->skip_list.nodes[index].count++;
2112 return;
2113 }
2114 AddNodePixelList(pixel_list,index);
2115}
2116
2117static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
2118{
2119 if (x < 0)
2120 return(-x);
2121 return(x);
2122}
2123
2124static inline size_t MagickMax(const size_t x,const size_t y)
2125{
2126 if (x > y)
2127 return(x);
2128 return(y);
2129}
2130
2131static void ResetPixelList(PixelList *pixel_list)
2132{
2133 int
2134 level;
2135
2136 register SkipNode
2137 *root;
2138
2139 register SkipList
2140 *p;
2141
2142 /*
2143 Reset the skip-list.
2144 */
2145 p=(&pixel_list->skip_list);
2146 root=p->nodes+65536UL;
2147 p->level=0;
2148 for (level=0; level < 9; level++)
2149 root->next[level]=65536UL;
2150 pixel_list->seed=pixel_list->signature++;
2151}
2152
2153MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
2154 const size_t width,const size_t height,ExceptionInfo *exception)
2155{
2156#define StatisticImageTag "Statistic/Image"
2157
2158 CacheView
2159 *image_view,
2160 *statistic_view;
2161
2162 Image
2163 *statistic_image;
2164
2165 MagickBooleanType
2166 status;
2167
2168 MagickOffsetType
2169 progress;
2170
2171 PixelList
2172 **restrict pixel_list;
2173
2174 ssize_t
2175 center,
2176 y;
2177
2178 /*
2179 Initialize statistics image attributes.
2180 */
2181 assert(image != (Image *) NULL);
2182 assert(image->signature == MagickSignature);
2183 if (image->debug != MagickFalse)
2184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2185 assert(exception != (ExceptionInfo *) NULL);
2186 assert(exception->signature == MagickSignature);
2187 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2188 exception);
2189 if (statistic_image == (Image *) NULL)
2190 return((Image *) NULL);
2191 status=SetImageStorageClass(statistic_image,DirectClass,exception);
2192 if (status == MagickFalse)
2193 {
2194 statistic_image=DestroyImage(statistic_image);
2195 return((Image *) NULL);
2196 }
2197 pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
2198 if (pixel_list == (PixelList **) NULL)
2199 {
2200 statistic_image=DestroyImage(statistic_image);
2201 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2202 }
2203 /*
2204 Make each pixel the min / max / median / mode / etc. of the neighborhood.
2205 */
2206 center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
2207 (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
2208 status=MagickTrue;
2209 progress=0;
2210 image_view=AcquireCacheView(image);
2211 statistic_view=AcquireCacheView(statistic_image);
2212#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002213 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy99bd5232011-12-07 14:38:20 +00002214#endif
2215 for (y=0; y < (ssize_t) statistic_image->rows; y++)
2216 {
2217 const int
2218 id = GetOpenMPThreadId();
2219
2220 register const Quantum
2221 *restrict p;
2222
2223 register Quantum
2224 *restrict q;
2225
2226 register ssize_t
2227 x;
2228
2229 if (status == MagickFalse)
2230 continue;
2231 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
2232 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
2233 MagickMax(height,1),exception);
2234 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
2235 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2236 {
2237 status=MagickFalse;
2238 continue;
2239 }
2240 for (x=0; x < (ssize_t) statistic_image->columns; x++)
2241 {
2242 register ssize_t
2243 i;
2244
2245 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2246 {
2247 PixelChannel
2248 channel;
2249
2250 PixelTrait
2251 statistic_traits,
2252 traits;
2253
2254 Quantum
2255 pixel;
2256
2257 register const Quantum
2258 *restrict pixels;
2259
2260 register ssize_t
2261 u;
2262
2263 ssize_t
2264 v;
2265
cristy99bd5232011-12-07 14:38:20 +00002266 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00002267 traits=GetPixelChannelMapTraits(image,channel);
cristy99bd5232011-12-07 14:38:20 +00002268 statistic_traits=GetPixelChannelMapTraits(statistic_image,channel);
2269 if ((traits == UndefinedPixelTrait) ||
2270 (statistic_traits == UndefinedPixelTrait))
2271 continue;
2272 if ((statistic_traits & CopyPixelTrait) != 0)
2273 {
2274 SetPixelChannel(statistic_image,channel,p[center+i],q);
2275 continue;
2276 }
2277 pixels=p;
2278 ResetPixelList(pixel_list[id]);
2279 for (v=0; v < (ssize_t) MagickMax(height,1); v++)
2280 {
2281 for (u=0; u < (ssize_t) MagickMax(width,1); u++)
2282 {
2283 InsertPixelList(image,pixels[i],pixel_list[id]);
2284 pixels+=GetPixelChannels(image);
2285 }
2286 pixels+=image->columns*GetPixelChannels(image);
2287 }
2288 switch (type)
2289 {
2290 case GradientStatistic:
2291 {
2292 MagickRealType
2293 maximum,
2294 minimum;
2295
2296 GetMinimumPixelList(pixel_list[id],&pixel);
2297 minimum=(MagickRealType) pixel;
2298 GetMaximumPixelList(pixel_list[id],&pixel);
2299 maximum=(MagickRealType) pixel;
2300 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
2301 break;
2302 }
2303 case MaximumStatistic:
2304 {
2305 GetMaximumPixelList(pixel_list[id],&pixel);
2306 break;
2307 }
2308 case MeanStatistic:
2309 {
2310 GetMeanPixelList(pixel_list[id],&pixel);
2311 break;
2312 }
2313 case MedianStatistic:
2314 default:
2315 {
2316 GetMedianPixelList(pixel_list[id],&pixel);
2317 break;
2318 }
2319 case MinimumStatistic:
2320 {
2321 GetMinimumPixelList(pixel_list[id],&pixel);
2322 break;
2323 }
2324 case ModeStatistic:
2325 {
2326 GetModePixelList(pixel_list[id],&pixel);
2327 break;
2328 }
2329 case NonpeakStatistic:
2330 {
2331 GetNonpeakPixelList(pixel_list[id],&pixel);
2332 break;
2333 }
2334 case StandardDeviationStatistic:
2335 {
2336 GetStandardDeviationPixelList(pixel_list[id],&pixel);
2337 break;
2338 }
2339 }
2340 SetPixelChannel(statistic_image,channel,pixel,q);
2341 }
2342 p+=GetPixelChannels(image);
2343 q+=GetPixelChannels(statistic_image);
2344 }
2345 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
2346 status=MagickFalse;
2347 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2348 {
2349 MagickBooleanType
2350 proceed;
2351
2352#if defined(MAGICKCORE_OPENMP_SUPPORT)
2353 #pragma omp critical (MagickCore_StatisticImage)
2354#endif
2355 proceed=SetImageProgress(image,StatisticImageTag,progress++,
2356 image->rows);
2357 if (proceed == MagickFalse)
2358 status=MagickFalse;
2359 }
2360 }
2361 statistic_view=DestroyCacheView(statistic_view);
2362 image_view=DestroyCacheView(image_view);
2363 pixel_list=DestroyPixelListThreadSet(pixel_list);
2364 return(statistic_image);
2365}