statistic.c revision 1eced097f7f4f51d5aa14d46079292fa1dcf0e77
1/* 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% % 13% MagickCore Image Statistical Methods % 14% % 15% Software Design % 16% John Cristy % 17% July 1992 % 18% % 19% % 20% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 21% 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*/ 43#include "MagickCore/studio.h" 44#include "MagickCore/property.h" 45#include "MagickCore/animate.h" 46#include "MagickCore/blob.h" 47#include "MagickCore/blob-private.h" 48#include "MagickCore/cache.h" 49#include "MagickCore/cache-private.h" 50#include "MagickCore/cache-view.h" 51#include "MagickCore/client.h" 52#include "MagickCore/color.h" 53#include "MagickCore/color-private.h" 54#include "MagickCore/colorspace.h" 55#include "MagickCore/colorspace-private.h" 56#include "MagickCore/composite.h" 57#include "MagickCore/composite-private.h" 58#include "MagickCore/compress.h" 59#include "MagickCore/constitute.h" 60#include "MagickCore/display.h" 61#include "MagickCore/draw.h" 62#include "MagickCore/enhance.h" 63#include "MagickCore/exception.h" 64#include "MagickCore/exception-private.h" 65#include "MagickCore/gem.h" 66#include "MagickCore/gem-private.h" 67#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/resource_.h" 85#include "MagickCore/segment.h" 86#include "MagickCore/semaphore.h" 87#include "MagickCore/signature-private.h" 88#include "MagickCore/statistic.h" 89#include "MagickCore/string_.h" 90#include "MagickCore/thread-private.h" 91#include "MagickCore/timer.h" 92#include "MagickCore/utility.h" 93#include "MagickCore/version.h" 94 95/* 96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 97% % 98% % 99% % 100% E v a l u a t e I m a g e % 101% % 102% % 103% % 104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 105% 106% EvaluateImage() applies a value to the image with an arithmetic, relational, 107% or logical operator to an image. Use these operations to lighten or darken 108% an image, to increase or decrease contrast in an image, or to produce the 109% "negative" of an image. 110% 111% The format of the EvaluateImage method is: 112% 113% MagickBooleanType EvaluateImage(Image *image, 114% const MagickEvaluateOperator op,const double value, 115% ExceptionInfo *exception) 116% MagickBooleanType EvaluateImages(Image *images, 117% const MagickEvaluateOperator op,const double value, 118% ExceptionInfo *exception) 119% 120% A description of each parameter follows: 121% 122% o image: the image. 123% 124% o op: A channel op. 125% 126% o value: A value value. 127% 128% o exception: return any errors or warnings in this structure. 129% 130*/ 131 132typedef struct _PixelChannels 133{ 134 double 135 channel[CompositePixelChannel]; 136} PixelChannels; 137 138static PixelChannels **DestroyPixelThreadSet(PixelChannels **pixels) 139{ 140 register ssize_t 141 i; 142 143 assert(pixels != (PixelChannels **) NULL); 144 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) 145 if (pixels[i] != (PixelChannels *) NULL) 146 pixels[i]=(PixelChannels *) RelinquishMagickMemory(pixels[i]); 147 pixels=(PixelChannels **) RelinquishMagickMemory(pixels); 148 return(pixels); 149} 150 151static PixelChannels **AcquirePixelThreadSet(const Image *image, 152 const size_t number_images) 153{ 154 register ssize_t 155 i; 156 157 PixelChannels 158 **pixels; 159 160 size_t 161 length, 162 number_threads; 163 164 number_threads=(size_t) GetMagickResourceLimit(ThreadResource); 165 pixels=(PixelChannels **) AcquireQuantumMemory(number_threads, 166 sizeof(*pixels)); 167 if (pixels == (PixelChannels **) NULL) 168 return((PixelChannels **) NULL); 169 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels)); 170 for (i=0; i < (ssize_t) number_threads; i++) 171 { 172 register ssize_t 173 j; 174 175 length=image->columns; 176 if (length < number_images) 177 length=number_images; 178 pixels[i]=(PixelChannels *) AcquireQuantumMemory(length,sizeof(**pixels)); 179 if (pixels[i] == (PixelChannels *) NULL) 180 return(DestroyPixelThreadSet(pixels)); 181 for (j=0; j < (ssize_t) length; j++) 182 { 183 register ssize_t 184 k; 185 186 for (k=0; k < MaxPixelChannels; k++) 187 pixels[i][j].channel[k]=0.0; 188 } 189 } 190 return(pixels); 191} 192 193static inline double EvaluateMax(const double x,const double y) 194{ 195 if (x > y) 196 return(x); 197 return(y); 198} 199 200#if defined(__cplusplus) || defined(c_plusplus) 201extern "C" { 202#endif 203 204static int IntensityCompare(const void *x,const void *y) 205{ 206 const PixelChannels 207 *color_1, 208 *color_2; 209 210 double 211 distance; 212 213 register ssize_t 214 i; 215 216 color_1=(const PixelChannels *) x; 217 color_2=(const PixelChannels *) y; 218 distance=0.0; 219 for (i=0; i < MaxPixelChannels; i++) 220 distance+=color_1->channel[i]-(double) color_2->channel[i]; 221 return(distance < 0 ? -1 : distance > 0 ? 1 : 0); 222} 223 224#if defined(__cplusplus) || defined(c_plusplus) 225} 226#endif 227 228static inline double MagickMin(const double x,const double y) 229{ 230 if (x < y) 231 return(x); 232 return(y); 233} 234 235static double ApplyEvaluateOperator(RandomInfo *random_info, 236 Quantum pixel,const MagickEvaluateOperator op,const double value) 237{ 238 double 239 result; 240 241 result=0.0; 242 switch (op) 243 { 244 case UndefinedEvaluateOperator: 245 break; 246 case AbsEvaluateOperator: 247 { 248 result=(double) fabs((double) (pixel+value)); 249 break; 250 } 251 case AddEvaluateOperator: 252 { 253 result=(double) (pixel+value); 254 break; 255 } 256 case AddModulusEvaluateOperator: 257 { 258 /* 259 This returns a 'floored modulus' of the addition which is a positive 260 result. It differs from % or fmod() that returns a 'truncated modulus' 261 result, where floor() is replaced by trunc() and could return a 262 negative result (which is clipped). 263 */ 264 result=pixel+value; 265 result-=(QuantumRange+1.0)*floor((double) result/(QuantumRange+1.0)); 266 break; 267 } 268 case AndEvaluateOperator: 269 { 270 result=(double) ((size_t) pixel & (size_t) (value+0.5)); 271 break; 272 } 273 case CosineEvaluateOperator: 274 { 275 result=(double) (QuantumRange*(0.5*cos((double) (2.0*MagickPI* 276 QuantumScale*pixel*value))+0.5)); 277 break; 278 } 279 case DivideEvaluateOperator: 280 { 281 result=pixel/(value == 0.0 ? 1.0 : value); 282 break; 283 } 284 case ExponentialEvaluateOperator: 285 { 286 result=(double) (QuantumRange*exp((double) (value*QuantumScale* 287 pixel))); 288 break; 289 } 290 case GaussianNoiseEvaluateOperator: 291 { 292 result=(double) GenerateDifferentialNoise(random_info,pixel, 293 GaussianNoise,value); 294 break; 295 } 296 case ImpulseNoiseEvaluateOperator: 297 { 298 result=(double) GenerateDifferentialNoise(random_info,pixel, 299 ImpulseNoise,value); 300 break; 301 } 302 case LaplacianNoiseEvaluateOperator: 303 { 304 result=(double) GenerateDifferentialNoise(random_info,pixel, 305 LaplacianNoise,value); 306 break; 307 } 308 case LeftShiftEvaluateOperator: 309 { 310 result=(double) ((size_t) pixel << (size_t) (value+0.5)); 311 break; 312 } 313 case LogEvaluateOperator: 314 { 315 if ((QuantumScale*pixel) >= MagickEpsilon) 316 result=(double) (QuantumRange*log((double) (QuantumScale*value* 317 pixel+1.0))/log((double) (value+1.0))); 318 break; 319 } 320 case MaxEvaluateOperator: 321 { 322 result=(double) EvaluateMax((double) pixel,value); 323 break; 324 } 325 case MeanEvaluateOperator: 326 { 327 result=(double) (pixel+value); 328 break; 329 } 330 case MedianEvaluateOperator: 331 { 332 result=(double) (pixel+value); 333 break; 334 } 335 case MinEvaluateOperator: 336 { 337 result=(double) MagickMin((double) pixel,value); 338 break; 339 } 340 case MultiplicativeNoiseEvaluateOperator: 341 { 342 result=(double) GenerateDifferentialNoise(random_info,pixel, 343 MultiplicativeGaussianNoise,value); 344 break; 345 } 346 case MultiplyEvaluateOperator: 347 { 348 result=(double) (value*pixel); 349 break; 350 } 351 case OrEvaluateOperator: 352 { 353 result=(double) ((size_t) pixel | (size_t) (value+0.5)); 354 break; 355 } 356 case PoissonNoiseEvaluateOperator: 357 { 358 result=(double) GenerateDifferentialNoise(random_info,pixel, 359 PoissonNoise,value); 360 break; 361 } 362 case PowEvaluateOperator: 363 { 364 result=(double) (QuantumRange*pow((double) (QuantumScale*pixel), 365 (double) value)); 366 break; 367 } 368 case RightShiftEvaluateOperator: 369 { 370 result=(double) ((size_t) pixel >> (size_t) (value+0.5)); 371 break; 372 } 373 case SetEvaluateOperator: 374 { 375 result=value; 376 break; 377 } 378 case SineEvaluateOperator: 379 { 380 result=(double) (QuantumRange*(0.5*sin((double) (2.0*MagickPI* 381 QuantumScale*pixel*value))+0.5)); 382 break; 383 } 384 case SubtractEvaluateOperator: 385 { 386 result=(double) (pixel-value); 387 break; 388 } 389 case SumEvaluateOperator: 390 { 391 result=(double) (pixel+value); 392 break; 393 } 394 case ThresholdEvaluateOperator: 395 { 396 result=(double) (((double) pixel <= value) ? 0 : 397 QuantumRange); 398 break; 399 } 400 case ThresholdBlackEvaluateOperator: 401 { 402 result=(double) (((double) pixel <= value) ? 0 : pixel); 403 break; 404 } 405 case ThresholdWhiteEvaluateOperator: 406 { 407 result=(double) (((double) pixel > value) ? QuantumRange : 408 pixel); 409 break; 410 } 411 case UniformNoiseEvaluateOperator: 412 { 413 result=(double) GenerateDifferentialNoise(random_info,pixel, 414 UniformNoise,value); 415 break; 416 } 417 case XorEvaluateOperator: 418 { 419 result=(double) ((size_t) pixel ^ (size_t) (value+0.5)); 420 break; 421 } 422 } 423 return(result); 424} 425 426MagickExport Image *EvaluateImages(const Image *images, 427 const MagickEvaluateOperator op,ExceptionInfo *exception) 428{ 429#define EvaluateImageTag "Evaluate/Image" 430 431 CacheView 432 *evaluate_view; 433 434 const Image 435 *next; 436 437 Image 438 *image; 439 440 MagickBooleanType 441 status; 442 443 MagickOffsetType 444 progress; 445 446 PixelChannels 447 **restrict evaluate_pixels; 448 449 RandomInfo 450 **restrict random_info; 451 452 size_t 453 number_images; 454 455 ssize_t 456 y; 457 458 unsigned long 459 key; 460 461 /* 462 Ensure the image are the same size. 463 */ 464 assert(images != (Image *) NULL); 465 assert(images->signature == MagickSignature); 466 if (images->debug != MagickFalse) 467 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); 468 assert(exception != (ExceptionInfo *) NULL); 469 assert(exception->signature == MagickSignature); 470 for (next=images; next != (Image *) NULL; next=GetNextImageInList(next)) 471 if ((next->columns != images->columns) || (next->rows != images->rows)) 472 { 473 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 474 "ImageWidthsOrHeightsDiffer","'%s'",images->filename); 475 return((Image *) NULL); 476 } 477 /* 478 Initialize evaluate next attributes. 479 */ 480 image=CloneImage(images,images->columns,images->rows,MagickTrue, 481 exception); 482 if (image == (Image *) NULL) 483 return((Image *) NULL); 484 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 485 { 486 image=DestroyImage(image); 487 return((Image *) NULL); 488 } 489 number_images=GetImageListLength(images); 490 evaluate_pixels=AcquirePixelThreadSet(images,number_images); 491 if (evaluate_pixels == (PixelChannels **) NULL) 492 { 493 image=DestroyImage(image); 494 (void) ThrowMagickException(exception,GetMagickModule(), 495 ResourceLimitError,"MemoryAllocationFailed","'%s'",images->filename); 496 return((Image *) NULL); 497 } 498 /* 499 Evaluate image pixels. 500 */ 501 status=MagickTrue; 502 progress=0; 503 random_info=AcquireRandomInfoThreadSet(); 504 key=GetRandomSecretKey(random_info[0]); 505 evaluate_view=AcquireAuthenticCacheView(image,exception); 506 if (op == MedianEvaluateOperator) 507 { 508#if defined(MAGICKCORE_OPENMP_SUPPORT) 509 #pragma omp parallel for schedule(static) shared(progress,status) \ 510 dynamic_number_threads(image,image->columns,image->rows,key == ~0UL) 511#endif 512 for (y=0; y < (ssize_t) image->rows; y++) 513 { 514 CacheView 515 *image_view; 516 517 const Image 518 *next; 519 520 const int 521 id = GetOpenMPThreadId(); 522 523 register PixelChannels 524 *evaluate_pixel; 525 526 register Quantum 527 *restrict q; 528 529 register ssize_t 530 x; 531 532 if (status == MagickFalse) 533 continue; 534 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y, 535 image->columns,1,exception); 536 if (q == (Quantum *) NULL) 537 { 538 status=MagickFalse; 539 continue; 540 } 541 evaluate_pixel=evaluate_pixels[id]; 542 for (x=0; x < (ssize_t) image->columns; x++) 543 { 544 register ssize_t 545 j, 546 k; 547 548 for (j=0; j < (ssize_t) number_images; j++) 549 for (k=0; k < MaxPixelChannels; k++) 550 evaluate_pixel[j].channel[k]=0.0; 551 next=images; 552 for (j=0; j < (ssize_t) number_images; j++) 553 { 554 register const Quantum 555 *p; 556 557 register ssize_t 558 i; 559 560 image_view=AcquireVirtualCacheView(next,exception); 561 p=GetCacheViewVirtualPixels(image_view,x,y,1,1,exception); 562 if (p == (const Quantum *) NULL) 563 { 564 image_view=DestroyCacheView(image_view); 565 break; 566 } 567 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 568 { 569 PixelChannel 570 channel; 571 572 PixelTrait 573 evaluate_traits, 574 traits; 575 576 channel=GetPixelChannelMapChannel(image,i); 577 evaluate_traits=GetPixelChannelMapTraits(image,channel); 578 traits=GetPixelChannelMapTraits(next,channel); 579 if ((traits == UndefinedPixelTrait) || 580 (evaluate_traits == UndefinedPixelTrait)) 581 continue; 582 if ((evaluate_traits & UpdatePixelTrait) == 0) 583 continue; 584 evaluate_pixel[j].channel[i]=ApplyEvaluateOperator( 585 random_info[id],GetPixelChannel(image,channel,p),op, 586 evaluate_pixel[j].channel[i]); 587 } 588 image_view=DestroyCacheView(image_view); 589 next=GetNextImageInList(next); 590 } 591 qsort((void *) evaluate_pixel,number_images,sizeof(*evaluate_pixel), 592 IntensityCompare); 593 for (k=0; k < (ssize_t) GetPixelChannels(image); k++) 594 q[k]=ClampToQuantum(evaluate_pixel[j/2].channel[k]); 595 q+=GetPixelChannels(image); 596 } 597 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse) 598 status=MagickFalse; 599 if (images->progress_monitor != (MagickProgressMonitor) NULL) 600 { 601 MagickBooleanType 602 proceed; 603 604#if defined(MAGICKCORE_OPENMP_SUPPORT) 605 #pragma omp critical (MagickCore_EvaluateImages) 606#endif 607 proceed=SetImageProgress(images,EvaluateImageTag,progress++, 608 image->rows); 609 if (proceed == MagickFalse) 610 status=MagickFalse; 611 } 612 } 613 } 614 else 615 { 616#if defined(MAGICKCORE_OPENMP_SUPPORT) 617 #pragma omp parallel for schedule(static) shared(progress,status) \ 618 dynamic_number_threads(image,image->columns,image->rows,key == ~0UL) 619#endif 620 for (y=0; y < (ssize_t) image->rows; y++) 621 { 622 CacheView 623 *image_view; 624 625 const Image 626 *next; 627 628 const int 629 id = GetOpenMPThreadId(); 630 631 register ssize_t 632 i, 633 x; 634 635 register PixelChannels 636 *evaluate_pixel; 637 638 register Quantum 639 *restrict q; 640 641 ssize_t 642 j; 643 644 if (status == MagickFalse) 645 continue; 646 q=QueueCacheViewAuthenticPixels(evaluate_view,0,y, 647 image->columns,1,exception); 648 if (q == (Quantum *) NULL) 649 { 650 status=MagickFalse; 651 continue; 652 } 653 evaluate_pixel=evaluate_pixels[id]; 654 for (j=0; j < (ssize_t) image->columns; j++) 655 for (i=0; i < MaxPixelChannels; i++) 656 evaluate_pixel[j].channel[i]=0.0; 657 next=images; 658 for (j=0; j < (ssize_t) number_images; j++) 659 { 660 register const Quantum 661 *p; 662 663 image_view=AcquireVirtualCacheView(next,exception); 664 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception); 665 if (p == (const Quantum *) NULL) 666 { 667 image_view=DestroyCacheView(image_view); 668 break; 669 } 670 for (x=0; x < (ssize_t) next->columns; x++) 671 { 672 register ssize_t 673 i; 674 675 if (GetPixelMask(next,p) != 0) 676 { 677 p+=GetPixelChannels(next); 678 continue; 679 } 680 for (i=0; i < (ssize_t) GetPixelChannels(next); i++) 681 { 682 PixelChannel 683 channel; 684 685 PixelTrait 686 evaluate_traits, 687 traits; 688 689 channel=GetPixelChannelMapChannel(image,i); 690 traits=GetPixelChannelMapTraits(next,channel); 691 evaluate_traits=GetPixelChannelMapTraits(image,channel); 692 if ((traits == UndefinedPixelTrait) || 693 (evaluate_traits == UndefinedPixelTrait)) 694 continue; 695 if ((traits & UpdatePixelTrait) == 0) 696 continue; 697 evaluate_pixel[x].channel[i]=ApplyEvaluateOperator( 698 random_info[id],GetPixelChannel(image,channel,p),j == 699 0 ? AddEvaluateOperator : op,evaluate_pixel[x].channel[i]); 700 } 701 p+=GetPixelChannels(next); 702 } 703 image_view=DestroyCacheView(image_view); 704 next=GetNextImageInList(next); 705 } 706 for (x=0; x < (ssize_t) image->columns; x++) 707 { 708 register ssize_t 709 i; 710 711 switch (op) 712 { 713 case MeanEvaluateOperator: 714 { 715 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 716 evaluate_pixel[x].channel[i]/=(double) number_images; 717 break; 718 } 719 case MultiplyEvaluateOperator: 720 { 721 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 722 { 723 register ssize_t 724 j; 725 726 for (j=0; j < (ssize_t) (number_images-1); j++) 727 evaluate_pixel[x].channel[i]*=QuantumScale; 728 } 729 break; 730 } 731 default: 732 break; 733 } 734 } 735 for (x=0; x < (ssize_t) image->columns; x++) 736 { 737 register ssize_t 738 i; 739 740 if (GetPixelMask(image,q) != 0) 741 { 742 q+=GetPixelChannels(image); 743 continue; 744 } 745 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 746 { 747 PixelChannel 748 channel; 749 750 PixelTrait 751 traits; 752 753 channel=GetPixelChannelMapChannel(image,i); 754 traits=GetPixelChannelMapTraits(image,channel); 755 if (traits == UndefinedPixelTrait) 756 continue; 757 if ((traits & UpdatePixelTrait) == 0) 758 continue; 759 q[i]=ClampToQuantum(evaluate_pixel[x].channel[i]); 760 } 761 q+=GetPixelChannels(image); 762 } 763 if (SyncCacheViewAuthenticPixels(evaluate_view,exception) == MagickFalse) 764 status=MagickFalse; 765 if (images->progress_monitor != (MagickProgressMonitor) NULL) 766 { 767 MagickBooleanType 768 proceed; 769 770#if defined(MAGICKCORE_OPENMP_SUPPORT) 771 #pragma omp critical (MagickCore_EvaluateImages) 772#endif 773 proceed=SetImageProgress(images,EvaluateImageTag,progress++, 774 image->rows); 775 if (proceed == MagickFalse) 776 status=MagickFalse; 777 } 778 } 779 } 780 evaluate_view=DestroyCacheView(evaluate_view); 781 evaluate_pixels=DestroyPixelThreadSet(evaluate_pixels); 782 random_info=DestroyRandomInfoThreadSet(random_info); 783 if (status == MagickFalse) 784 image=DestroyImage(image); 785 return(image); 786} 787 788MagickExport MagickBooleanType EvaluateImage(Image *image, 789 const MagickEvaluateOperator op,const double value,ExceptionInfo *exception) 790{ 791 CacheView 792 *image_view; 793 794 MagickBooleanType 795 status; 796 797 MagickOffsetType 798 progress; 799 800 RandomInfo 801 **restrict random_info; 802 803 ssize_t 804 y; 805 806 unsigned long 807 key; 808 809 assert(image != (Image *) NULL); 810 assert(image->signature == MagickSignature); 811 if (image->debug != MagickFalse) 812 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 813 assert(exception != (ExceptionInfo *) NULL); 814 assert(exception->signature == MagickSignature); 815 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 816 return(MagickFalse); 817 status=MagickTrue; 818 progress=0; 819 random_info=AcquireRandomInfoThreadSet(); 820 key=GetRandomSecretKey(random_info[0]); 821 image_view=AcquireAuthenticCacheView(image,exception); 822#if defined(MAGICKCORE_OPENMP_SUPPORT) 823 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 824 dynamic_number_threads(image,image->columns,image->rows,key == ~0UL) 825#endif 826 for (y=0; y < (ssize_t) image->rows; y++) 827 { 828 const int 829 id = GetOpenMPThreadId(); 830 831 register Quantum 832 *restrict q; 833 834 register ssize_t 835 x; 836 837 if (status == MagickFalse) 838 continue; 839 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 840 if (q == (Quantum *) NULL) 841 { 842 status=MagickFalse; 843 continue; 844 } 845 for (x=0; x < (ssize_t) image->columns; x++) 846 { 847 register ssize_t 848 i; 849 850 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 851 { 852 PixelChannel 853 channel; 854 855 PixelTrait 856 traits; 857 858 channel=GetPixelChannelMapChannel(image,i); 859 traits=GetPixelChannelMapTraits(image,channel); 860 if (traits == UndefinedPixelTrait) 861 continue; 862 if (((traits & CopyPixelTrait) != 0) || 863 (GetPixelMask(image,q) != 0)) 864 continue; 865 q[i]=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q[i],op, 866 value)); 867 } 868 q+=GetPixelChannels(image); 869 } 870 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 871 status=MagickFalse; 872 if (image->progress_monitor != (MagickProgressMonitor) NULL) 873 { 874 MagickBooleanType 875 proceed; 876 877#if defined(MAGICKCORE_OPENMP_SUPPORT) 878 #pragma omp critical (MagickCore_EvaluateImage) 879#endif 880 proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows); 881 if (proceed == MagickFalse) 882 status=MagickFalse; 883 } 884 } 885 image_view=DestroyCacheView(image_view); 886 random_info=DestroyRandomInfoThreadSet(random_info); 887 return(status); 888} 889 890/* 891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 892% % 893% % 894% % 895% F u n c t i o n I m a g e % 896% % 897% % 898% % 899%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 900% 901% FunctionImage() applies a value to the image with an arithmetic, relational, 902% or logical operator to an image. Use these operations to lighten or darken 903% an image, to increase or decrease contrast in an image, or to produce the 904% "negative" of an image. 905% 906% The format of the FunctionImage method is: 907% 908% MagickBooleanType FunctionImage(Image *image, 909% const MagickFunction function,const ssize_t number_parameters, 910% const double *parameters,ExceptionInfo *exception) 911% 912% A description of each parameter follows: 913% 914% o image: the image. 915% 916% o function: A channel function. 917% 918% o parameters: one or more parameters. 919% 920% o exception: return any errors or warnings in this structure. 921% 922*/ 923 924static Quantum ApplyFunction(Quantum pixel,const MagickFunction function, 925 const size_t number_parameters,const double *parameters, 926 ExceptionInfo *exception) 927{ 928 double 929 result; 930 931 register ssize_t 932 i; 933 934 (void) exception; 935 result=0.0; 936 switch (function) 937 { 938 case PolynomialFunction: 939 { 940 /* 941 Polynomial: polynomial constants, highest to lowest order (e.g. c0*x^3+ 942 c1*x^2+c2*x+c3). 943 */ 944 result=0.0; 945 for (i=0; i < (ssize_t) number_parameters; i++) 946 result=result*QuantumScale*pixel+parameters[i]; 947 result*=QuantumRange; 948 break; 949 } 950 case SinusoidFunction: 951 { 952 double 953 amplitude, 954 bias, 955 frequency, 956 phase; 957 958 /* 959 Sinusoid: frequency, phase, amplitude, bias. 960 */ 961 frequency=(number_parameters >= 1) ? parameters[0] : 1.0; 962 phase=(number_parameters >= 2) ? parameters[1] : 0.0; 963 amplitude=(number_parameters >= 3) ? parameters[2] : 0.5; 964 bias=(number_parameters >= 4) ? parameters[3] : 0.5; 965 result=(double) (QuantumRange*(amplitude*sin((double) (2.0* 966 MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias)); 967 break; 968 } 969 case ArcsinFunction: 970 { 971 double 972 bias, 973 center, 974 range, 975 width; 976 977 /* 978 Arcsin (peged at range limits for invalid results): width, center, 979 range, and bias. 980 */ 981 width=(number_parameters >= 1) ? parameters[0] : 1.0; 982 center=(number_parameters >= 2) ? parameters[1] : 0.5; 983 range=(number_parameters >= 3) ? parameters[2] : 1.0; 984 bias=(number_parameters >= 4) ? parameters[3] : 0.5; 985 result=2.0/width*(QuantumScale*pixel-center); 986 if ( result <= -1.0 ) 987 result=bias-range/2.0; 988 else 989 if (result >= 1.0) 990 result=bias+range/2.0; 991 else 992 result=(double) (range/MagickPI*asin((double) result)+bias); 993 result*=QuantumRange; 994 break; 995 } 996 case ArctanFunction: 997 { 998 double 999 center, 1000 bias, 1001 range, 1002 slope; 1003 1004 /* 1005 Arctan: slope, center, range, and bias. 1006 */ 1007 slope=(number_parameters >= 1) ? parameters[0] : 1.0; 1008 center=(number_parameters >= 2) ? parameters[1] : 0.5; 1009 range=(number_parameters >= 3) ? parameters[2] : 1.0; 1010 bias=(number_parameters >= 4) ? parameters[3] : 0.5; 1011 result=(double) (MagickPI*slope*(QuantumScale*pixel-center)); 1012 result=(double) (QuantumRange*(range/MagickPI*atan((double) 1013 result)+bias)); 1014 break; 1015 } 1016 case UndefinedFunction: 1017 break; 1018 } 1019 return(ClampToQuantum(result)); 1020} 1021 1022MagickExport MagickBooleanType FunctionImage(Image *image, 1023 const MagickFunction function,const size_t number_parameters, 1024 const double *parameters,ExceptionInfo *exception) 1025{ 1026#define FunctionImageTag "Function/Image " 1027 1028 CacheView 1029 *image_view; 1030 1031 MagickBooleanType 1032 status; 1033 1034 MagickOffsetType 1035 progress; 1036 1037 ssize_t 1038 y; 1039 1040 assert(image != (Image *) NULL); 1041 assert(image->signature == MagickSignature); 1042 if (image->debug != MagickFalse) 1043 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1044 assert(exception != (ExceptionInfo *) NULL); 1045 assert(exception->signature == MagickSignature); 1046 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 1047 return(MagickFalse); 1048 status=MagickTrue; 1049 progress=0; 1050 image_view=AcquireAuthenticCacheView(image,exception); 1051#if defined(MAGICKCORE_OPENMP_SUPPORT) 1052 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1053 dynamic_number_threads(image,image->columns,image->rows,1) 1054#endif 1055 for (y=0; y < (ssize_t) image->rows; y++) 1056 { 1057 register Quantum 1058 *restrict q; 1059 1060 register ssize_t 1061 x; 1062 1063 if (status == MagickFalse) 1064 continue; 1065 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 1066 if (q == (Quantum *) NULL) 1067 { 1068 status=MagickFalse; 1069 continue; 1070 } 1071 for (x=0; x < (ssize_t) image->columns; x++) 1072 { 1073 register ssize_t 1074 i; 1075 1076 if (GetPixelMask(image,q) != 0) 1077 { 1078 q+=GetPixelChannels(image); 1079 continue; 1080 } 1081 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1082 { 1083 PixelChannel 1084 channel; 1085 1086 PixelTrait 1087 traits; 1088 1089 channel=GetPixelChannelMapChannel(image,i); 1090 traits=GetPixelChannelMapTraits(image,channel); 1091 if (traits == UndefinedPixelTrait) 1092 continue; 1093 if ((traits & UpdatePixelTrait) == 0) 1094 continue; 1095 q[i]=ApplyFunction(q[i],function,number_parameters,parameters, 1096 exception); 1097 } 1098 q+=GetPixelChannels(image); 1099 } 1100 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1101 status=MagickFalse; 1102 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1103 { 1104 MagickBooleanType 1105 proceed; 1106 1107#if defined(MAGICKCORE_OPENMP_SUPPORT) 1108 #pragma omp critical (MagickCore_FunctionImage) 1109#endif 1110 proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows); 1111 if (proceed == MagickFalse) 1112 status=MagickFalse; 1113 } 1114 } 1115 image_view=DestroyCacheView(image_view); 1116 return(status); 1117} 1118 1119/* 1120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1121% % 1122% % 1123% % 1124% G e t I m a g e E x t r e m a % 1125% % 1126% % 1127% % 1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1129% 1130% GetImageExtrema() returns the extrema of one or more image channels. 1131% 1132% The format of the GetImageExtrema method is: 1133% 1134% MagickBooleanType GetImageExtrema(const Image *image,size_t *minima, 1135% size_t *maxima,ExceptionInfo *exception) 1136% 1137% A description of each parameter follows: 1138% 1139% o image: the image. 1140% 1141% o minima: the minimum value in the channel. 1142% 1143% o maxima: the maximum value in the channel. 1144% 1145% o exception: return any errors or warnings in this structure. 1146% 1147*/ 1148MagickExport MagickBooleanType GetImageExtrema(const Image *image, 1149 size_t *minima,size_t *maxima,ExceptionInfo *exception) 1150{ 1151 double 1152 max, 1153 min; 1154 1155 MagickBooleanType 1156 status; 1157 1158 assert(image != (Image *) NULL); 1159 assert(image->signature == MagickSignature); 1160 if (image->debug != MagickFalse) 1161 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1162 status=GetImageRange(image,&min,&max,exception); 1163 *minima=(size_t) ceil(min-0.5); 1164 *maxima=(size_t) floor(max+0.5); 1165 return(status); 1166} 1167 1168/* 1169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1170% % 1171% % 1172% % 1173% G e t I m a g e M e a n % 1174% % 1175% % 1176% % 1177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1178% 1179% GetImageMean() returns the mean and standard deviation of one or more image 1180% channels. 1181% 1182% The format of the GetImageMean method is: 1183% 1184% MagickBooleanType GetImageMean(const Image *image,double *mean, 1185% double *standard_deviation,ExceptionInfo *exception) 1186% 1187% A description of each parameter follows: 1188% 1189% o image: the image. 1190% 1191% o mean: the average value in the channel. 1192% 1193% o standard_deviation: the standard deviation of the channel. 1194% 1195% o exception: return any errors or warnings in this structure. 1196% 1197*/ 1198MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean, 1199 double *standard_deviation,ExceptionInfo *exception) 1200{ 1201 double 1202 area; 1203 1204 ChannelStatistics 1205 *channel_statistics; 1206 1207 register ssize_t 1208 i; 1209 1210 assert(image != (Image *) NULL); 1211 assert(image->signature == MagickSignature); 1212 if (image->debug != MagickFalse) 1213 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1214 channel_statistics=GetImageStatistics(image,exception); 1215 if (channel_statistics == (ChannelStatistics *) NULL) 1216 return(MagickFalse); 1217 area=0.0; 1218 channel_statistics[CompositePixelChannel].mean=0.0; 1219 channel_statistics[CompositePixelChannel].standard_deviation=0.0; 1220 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1221 { 1222 PixelChannel 1223 channel; 1224 1225 PixelTrait 1226 traits; 1227 1228 channel=GetPixelChannelMapChannel(image,i); 1229 traits=GetPixelChannelMapTraits(image,channel); 1230 if (traits == UndefinedPixelTrait) 1231 continue; 1232 if ((traits & UpdatePixelTrait) == 0) 1233 continue; 1234 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean; 1235 channel_statistics[CompositePixelChannel].standard_deviation+= 1236 channel_statistics[i].variance-channel_statistics[i].mean* 1237 channel_statistics[i].mean; 1238 area++; 1239 } 1240 channel_statistics[CompositePixelChannel].mean/=area; 1241 channel_statistics[CompositePixelChannel].standard_deviation= 1242 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/area); 1243 *mean=channel_statistics[CompositePixelChannel].mean; 1244 *standard_deviation= 1245 channel_statistics[CompositePixelChannel].standard_deviation; 1246 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory( 1247 channel_statistics); 1248 return(MagickTrue); 1249} 1250 1251/* 1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1253% % 1254% % 1255% % 1256% G e t I m a g e K u r t o s i s % 1257% % 1258% % 1259% % 1260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1261% 1262% GetImageKurtosis() returns the kurtosis and skewness of one or more 1263% image channels. 1264% 1265% The format of the GetImageKurtosis method is: 1266% 1267% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis, 1268% double *skewness,ExceptionInfo *exception) 1269% 1270% A description of each parameter follows: 1271% 1272% o image: the image. 1273% 1274% o kurtosis: the kurtosis of the channel. 1275% 1276% o skewness: the skewness of the channel. 1277% 1278% o exception: return any errors or warnings in this structure. 1279% 1280*/ 1281MagickExport MagickBooleanType GetImageKurtosis(const Image *image, 1282 double *kurtosis,double *skewness,ExceptionInfo *exception) 1283{ 1284 CacheView 1285 *image_view; 1286 1287 double 1288 area, 1289 mean, 1290 standard_deviation, 1291 sum_squares, 1292 sum_cubes, 1293 sum_fourth_power; 1294 1295 MagickBooleanType 1296 status; 1297 1298 ssize_t 1299 y; 1300 1301 assert(image != (Image *) NULL); 1302 assert(image->signature == MagickSignature); 1303 if (image->debug != MagickFalse) 1304 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1305 status=MagickTrue; 1306 *kurtosis=0.0; 1307 *skewness=0.0; 1308 area=0.0; 1309 mean=0.0; 1310 standard_deviation=0.0; 1311 sum_squares=0.0; 1312 sum_cubes=0.0; 1313 sum_fourth_power=0.0; 1314 image_view=AcquireVirtualCacheView(image,exception); 1315#if defined(MAGICKCORE_OPENMP_SUPPORT) 1316 #pragma omp parallel for schedule(static) shared(status) \ 1317 dynamic_number_threads(image,image->columns,image->rows,1) 1318#endif 1319 for (y=0; y < (ssize_t) image->rows; y++) 1320 { 1321 register const Quantum 1322 *restrict p; 1323 1324 register ssize_t 1325 x; 1326 1327 if (status == MagickFalse) 1328 continue; 1329 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1330 if (p == (const Quantum *) NULL) 1331 { 1332 status=MagickFalse; 1333 continue; 1334 } 1335 for (x=0; x < (ssize_t) image->columns; x++) 1336 { 1337 register ssize_t 1338 i; 1339 1340 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1341 { 1342 PixelChannel 1343 channel; 1344 1345 PixelTrait 1346 traits; 1347 1348 channel=GetPixelChannelMapChannel(image,i); 1349 traits=GetPixelChannelMapTraits(image,channel); 1350 if (traits == UndefinedPixelTrait) 1351 continue; 1352 if (((traits & UpdatePixelTrait) == 0) || 1353 (GetPixelMask(image,p) != 0)) 1354 continue; 1355#if defined(MAGICKCORE_OPENMP_SUPPORT) 1356 #pragma omp critical (MagickCore_GetImageKurtosis) 1357#endif 1358 { 1359 mean+=p[i]; 1360 sum_squares+=(double) p[i]*p[i]; 1361 sum_cubes+=(double) p[i]*p[i]*p[i]; 1362 sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i]; 1363 area++; 1364 } 1365 } 1366 p+=GetPixelChannels(image); 1367 } 1368 } 1369 image_view=DestroyCacheView(image_view); 1370 if (area != 0.0) 1371 { 1372 mean/=area; 1373 sum_squares/=area; 1374 sum_cubes/=area; 1375 sum_fourth_power/=area; 1376 } 1377 standard_deviation=sqrt(sum_squares-(mean*mean)); 1378 if (standard_deviation != 0.0) 1379 { 1380 *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares- 1381 3.0*mean*mean*mean*mean; 1382 *kurtosis/=standard_deviation*standard_deviation*standard_deviation* 1383 standard_deviation; 1384 *kurtosis-=3.0; 1385 *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean; 1386 *skewness/=standard_deviation*standard_deviation*standard_deviation; 1387 } 1388 return(status); 1389} 1390 1391/* 1392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1393% % 1394% % 1395% % 1396% G e t I m a g e R a n g e % 1397% % 1398% % 1399% % 1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1401% 1402% GetImageRange() returns the range of one or more image channels. 1403% 1404% The format of the GetImageRange method is: 1405% 1406% MagickBooleanType GetImageRange(const Image *image,double *minima, 1407% double *maxima,ExceptionInfo *exception) 1408% 1409% A description of each parameter follows: 1410% 1411% o image: the image. 1412% 1413% o minima: the minimum value in the channel. 1414% 1415% o maxima: the maximum value in the channel. 1416% 1417% o exception: return any errors or warnings in this structure. 1418% 1419*/ 1420MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima, 1421 double *maxima,ExceptionInfo *exception) 1422{ 1423 CacheView 1424 *image_view; 1425 1426 MagickBooleanType 1427 initialize, 1428 status; 1429 1430 ssize_t 1431 y; 1432 1433 assert(image != (Image *) NULL); 1434 assert(image->signature == MagickSignature); 1435 if (image->debug != MagickFalse) 1436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1437 status=MagickTrue; 1438 initialize=MagickTrue; 1439 *maxima=0.0; 1440 *minima=0.0; 1441 image_view=AcquireVirtualCacheView(image,exception); 1442#if defined(MAGICKCORE_OPENMP_SUPPORT) 1443 #pragma omp parallel for schedule(static) shared(status,initialize) \ 1444 dynamic_number_threads(image,image->columns,image->rows,1) 1445#endif 1446 for (y=0; y < (ssize_t) image->rows; y++) 1447 { 1448 register const Quantum 1449 *restrict p; 1450 1451 register ssize_t 1452 x; 1453 1454 if (status == MagickFalse) 1455 continue; 1456 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1457 if (p == (const Quantum *) NULL) 1458 { 1459 status=MagickFalse; 1460 continue; 1461 } 1462 for (x=0; x < (ssize_t) image->columns; x++) 1463 { 1464 register ssize_t 1465 i; 1466 1467 if (GetPixelMask(image,p) != 0) 1468 { 1469 p+=GetPixelChannels(image); 1470 continue; 1471 } 1472 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1473 { 1474 PixelChannel 1475 channel; 1476 1477 PixelTrait 1478 traits; 1479 1480 channel=GetPixelChannelMapChannel(image,i); 1481 traits=GetPixelChannelMapTraits(image,channel); 1482 if (traits == UndefinedPixelTrait) 1483 continue; 1484 if ((traits & UpdatePixelTrait) == 0) 1485 continue; 1486#if defined(MAGICKCORE_OPENMP_SUPPORT) 1487 #pragma omp critical (MagickCore_GetImageRange) 1488#endif 1489 { 1490 if (initialize != MagickFalse) 1491 { 1492 *minima=(double) p[i]; 1493 *maxima=(double) p[i]; 1494 initialize=MagickFalse; 1495 } 1496 else 1497 { 1498 if ((double) p[i] < *minima) 1499 *minima=(double) p[i]; 1500 if ((double) p[i] > *maxima) 1501 *maxima=(double) p[i]; 1502 } 1503 } 1504 } 1505 p+=GetPixelChannels(image); 1506 } 1507 } 1508 image_view=DestroyCacheView(image_view); 1509 return(status); 1510} 1511 1512/* 1513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1514% % 1515% % 1516% % 1517% G e t I m a g e S t a t i s t i c s % 1518% % 1519% % 1520% % 1521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1522% 1523% GetImageStatistics() returns statistics for each channel in the image. The 1524% statistics include the channel depth, its minima, maxima, mean, standard 1525% deviation, kurtosis and skewness. You can access the red channel mean, for 1526% example, like this: 1527% 1528% channel_statistics=GetImageStatistics(image,exception); 1529% red_mean=channel_statistics[RedPixelChannel].mean; 1530% 1531% Use MagickRelinquishMemory() to free the statistics buffer. 1532% 1533% The format of the GetImageStatistics method is: 1534% 1535% ChannelStatistics *GetImageStatistics(const Image *image, 1536% ExceptionInfo *exception) 1537% 1538% A description of each parameter follows: 1539% 1540% o image: the image. 1541% 1542% o exception: return any errors or warnings in this structure. 1543% 1544*/ 1545 1546static size_t GetImageChannels(const Image *image) 1547{ 1548 register ssize_t 1549 i; 1550 1551 size_t 1552 channels; 1553 1554 channels=0; 1555 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1556 { 1557 PixelChannel 1558 channel; 1559 1560 PixelTrait 1561 traits; 1562 1563 channel=GetPixelChannelMapChannel(image,i); 1564 traits=GetPixelChannelMapTraits(image,channel); 1565 if ((traits & UpdatePixelTrait) != 0) 1566 channels++; 1567 } 1568 return(channels); 1569} 1570 1571MagickExport ChannelStatistics *GetImageStatistics(const Image *image, 1572 ExceptionInfo *exception) 1573{ 1574 ChannelStatistics 1575 *channel_statistics; 1576 1577 MagickStatusType 1578 initialize, 1579 status; 1580 1581 QuantumAny 1582 range; 1583 1584 register ssize_t 1585 i; 1586 1587 size_t 1588 channels, 1589 depth; 1590 1591 ssize_t 1592 y; 1593 1594 assert(image != (Image *) NULL); 1595 assert(image->signature == MagickSignature); 1596 if (image->debug != MagickFalse) 1597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1598 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory( 1599 MaxPixelChannels+1,sizeof(*channel_statistics)); 1600 if (channel_statistics == (ChannelStatistics *) NULL) 1601 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 1602 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)* 1603 sizeof(*channel_statistics)); 1604 for (i=0; i <= (ssize_t) MaxPixelChannels; i++) 1605 channel_statistics[i].depth=1; 1606 initialize=MagickTrue; 1607 for (y=0; y < (ssize_t) image->rows; y++) 1608 { 1609 register const Quantum 1610 *restrict p; 1611 1612 register ssize_t 1613 x; 1614 1615 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1616 if (p == (const Quantum *) NULL) 1617 break; 1618 for (x=0; x < (ssize_t) image->columns; x++) 1619 { 1620 register ssize_t 1621 i; 1622 1623 if (GetPixelMask(image,p) != 0) 1624 { 1625 p+=GetPixelChannels(image); 1626 continue; 1627 } 1628 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1629 { 1630 PixelChannel 1631 channel; 1632 1633 PixelTrait 1634 traits; 1635 1636 channel=GetPixelChannelMapChannel(image,i); 1637 traits=GetPixelChannelMapTraits(image,channel); 1638 if (traits == UndefinedPixelTrait) 1639 continue; 1640 if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH) 1641 { 1642 depth=channel_statistics[channel].depth; 1643 range=GetQuantumRange(depth); 1644 status=p[i] != ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range), 1645 range) ? MagickTrue : MagickFalse; 1646 if (status != MagickFalse) 1647 { 1648 channel_statistics[channel].depth++; 1649 i--; 1650 continue; 1651 } 1652 } 1653 if (initialize != MagickFalse) 1654 { 1655 channel_statistics[channel].minima=(double) p[i]; 1656 channel_statistics[channel].maxima=(double) p[i]; 1657 initialize=MagickFalse; 1658 } 1659 else 1660 { 1661 if ((double) p[i] < channel_statistics[channel].minima) 1662 channel_statistics[channel].minima=(double) p[i]; 1663 if ((double) p[i] > channel_statistics[channel].maxima) 1664 channel_statistics[channel].maxima=(double) p[i]; 1665 } 1666 channel_statistics[channel].sum+=p[i]; 1667 channel_statistics[channel].sum_squared+=(double) p[i]*p[i]; 1668 channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i]; 1669 channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]* 1670 p[i]; 1671 channel_statistics[channel].area++; 1672 } 1673 p+=GetPixelChannels(image); 1674 } 1675 } 1676 for (i=0; i < (ssize_t) MaxPixelChannels; i++) 1677 { 1678 if (channel_statistics[i].area != 0.0) 1679 channel_statistics[i].sum/=channel_statistics[i].area; 1680 channel_statistics[i].sum_squared/=channel_statistics[i].area; 1681 channel_statistics[i].sum_cubed/=channel_statistics[i].area; 1682 channel_statistics[i].sum_fourth_power/=channel_statistics[i].area; 1683 channel_statistics[i].mean=channel_statistics[i].sum; 1684 channel_statistics[i].variance=channel_statistics[i].sum_squared; 1685 channel_statistics[i].standard_deviation=sqrt( 1686 channel_statistics[i].variance-(channel_statistics[i].mean* 1687 channel_statistics[i].mean)); 1688 } 1689 for (i=0; i < (ssize_t) MaxPixelChannels; i++) 1690 { 1691 channel_statistics[CompositePixelChannel].depth=(size_t) EvaluateMax( 1692 (double) channel_statistics[CompositePixelChannel].depth,(double) 1693 channel_statistics[i].depth); 1694 channel_statistics[CompositePixelChannel].minima=MagickMin( 1695 channel_statistics[CompositePixelChannel].minima, 1696 channel_statistics[i].minima); 1697 channel_statistics[CompositePixelChannel].maxima=EvaluateMax( 1698 channel_statistics[CompositePixelChannel].maxima, 1699 channel_statistics[i].maxima); 1700 channel_statistics[CompositePixelChannel].sum+=channel_statistics[i].sum; 1701 channel_statistics[CompositePixelChannel].sum_squared+= 1702 channel_statistics[i].sum_squared; 1703 channel_statistics[CompositePixelChannel].sum_cubed+= 1704 channel_statistics[i].sum_cubed; 1705 channel_statistics[CompositePixelChannel].sum_fourth_power+= 1706 channel_statistics[i].sum_fourth_power; 1707 channel_statistics[CompositePixelChannel].mean+=channel_statistics[i].mean; 1708 channel_statistics[CompositePixelChannel].variance+= 1709 channel_statistics[i].variance-channel_statistics[i].mean* 1710 channel_statistics[i].mean; 1711 channel_statistics[CompositePixelChannel].standard_deviation+= 1712 channel_statistics[i].variance-channel_statistics[i].mean* 1713 channel_statistics[i].mean; 1714 } 1715 channels=GetImageChannels(image); 1716 channel_statistics[CompositePixelChannel].sum/=channels; 1717 channel_statistics[CompositePixelChannel].sum_squared/=channels; 1718 channel_statistics[CompositePixelChannel].sum_cubed/=channels; 1719 channel_statistics[CompositePixelChannel].sum_fourth_power/=channels; 1720 channel_statistics[CompositePixelChannel].mean/=channels; 1721 channel_statistics[CompositePixelChannel].variance/=channels; 1722 channel_statistics[CompositePixelChannel].standard_deviation= 1723 sqrt(channel_statistics[CompositePixelChannel].standard_deviation/channels); 1724 channel_statistics[CompositePixelChannel].kurtosis/=channels; 1725 channel_statistics[CompositePixelChannel].skewness/=channels; 1726 for (i=0; i <= (ssize_t) MaxPixelChannels; i++) 1727 { 1728 if (channel_statistics[i].standard_deviation == 0.0) 1729 continue; 1730 channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0* 1731 channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0* 1732 channel_statistics[i].mean*channel_statistics[i].mean* 1733 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation* 1734 channel_statistics[i].standard_deviation* 1735 channel_statistics[i].standard_deviation); 1736 channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0* 1737 channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0* 1738 channel_statistics[i].mean*channel_statistics[i].mean* 1739 channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean* 1740 channel_statistics[i].mean*1.0*channel_statistics[i].mean* 1741 channel_statistics[i].mean)/(channel_statistics[i].standard_deviation* 1742 channel_statistics[i].standard_deviation* 1743 channel_statistics[i].standard_deviation* 1744 channel_statistics[i].standard_deviation)-3.0; 1745 } 1746 return(channel_statistics); 1747} 1748 1749/* 1750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1751% % 1752% % 1753% % 1754% S t a t i s t i c I m a g e % 1755% % 1756% % 1757% % 1758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1759% 1760% StatisticImage() makes each pixel the min / max / median / mode / etc. of 1761% the neighborhood of the specified width and height. 1762% 1763% The format of the StatisticImage method is: 1764% 1765% Image *StatisticImage(const Image *image,const StatisticType type, 1766% const size_t width,const size_t height,ExceptionInfo *exception) 1767% 1768% A description of each parameter follows: 1769% 1770% o image: the image. 1771% 1772% o type: the statistic type (median, mode, etc.). 1773% 1774% o width: the width of the pixel neighborhood. 1775% 1776% o height: the height of the pixel neighborhood. 1777% 1778% o exception: return any errors or warnings in this structure. 1779% 1780*/ 1781 1782typedef struct _SkipNode 1783{ 1784 size_t 1785 next[9], 1786 count, 1787 signature; 1788} SkipNode; 1789 1790typedef struct _SkipList 1791{ 1792 ssize_t 1793 level; 1794 1795 SkipNode 1796 *nodes; 1797} SkipList; 1798 1799typedef struct _PixelList 1800{ 1801 size_t 1802 length, 1803 seed; 1804 1805 SkipList 1806 skip_list; 1807 1808 size_t 1809 signature; 1810} PixelList; 1811 1812static PixelList *DestroyPixelList(PixelList *pixel_list) 1813{ 1814 if (pixel_list == (PixelList *) NULL) 1815 return((PixelList *) NULL); 1816 if (pixel_list->skip_list.nodes != (SkipNode *) NULL) 1817 pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory( 1818 pixel_list->skip_list.nodes); 1819 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list); 1820 return(pixel_list); 1821} 1822 1823static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list) 1824{ 1825 register ssize_t 1826 i; 1827 1828 assert(pixel_list != (PixelList **) NULL); 1829 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) 1830 if (pixel_list[i] != (PixelList *) NULL) 1831 pixel_list[i]=DestroyPixelList(pixel_list[i]); 1832 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list); 1833 return(pixel_list); 1834} 1835 1836static PixelList *AcquirePixelList(const size_t width,const size_t height) 1837{ 1838 PixelList 1839 *pixel_list; 1840 1841 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list)); 1842 if (pixel_list == (PixelList *) NULL) 1843 return(pixel_list); 1844 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list)); 1845 pixel_list->length=width*height; 1846 pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL, 1847 sizeof(*pixel_list->skip_list.nodes)); 1848 if (pixel_list->skip_list.nodes == (SkipNode *) NULL) 1849 return(DestroyPixelList(pixel_list)); 1850 (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL* 1851 sizeof(*pixel_list->skip_list.nodes)); 1852 pixel_list->signature=MagickSignature; 1853 return(pixel_list); 1854} 1855 1856static PixelList **AcquirePixelListThreadSet(const size_t width, 1857 const size_t height) 1858{ 1859 PixelList 1860 **pixel_list; 1861 1862 register ssize_t 1863 i; 1864 1865 size_t 1866 number_threads; 1867 1868 number_threads=(size_t) GetMagickResourceLimit(ThreadResource); 1869 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads, 1870 sizeof(*pixel_list)); 1871 if (pixel_list == (PixelList **) NULL) 1872 return((PixelList **) NULL); 1873 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list)); 1874 for (i=0; i < (ssize_t) number_threads; i++) 1875 { 1876 pixel_list[i]=AcquirePixelList(width,height); 1877 if (pixel_list[i] == (PixelList *) NULL) 1878 return(DestroyPixelListThreadSet(pixel_list)); 1879 } 1880 return(pixel_list); 1881} 1882 1883static void AddNodePixelList(PixelList *pixel_list,const size_t color) 1884{ 1885 register SkipList 1886 *p; 1887 1888 register ssize_t 1889 level; 1890 1891 size_t 1892 search, 1893 update[9]; 1894 1895 /* 1896 Initialize the node. 1897 */ 1898 p=(&pixel_list->skip_list); 1899 p->nodes[color].signature=pixel_list->signature; 1900 p->nodes[color].count=1; 1901 /* 1902 Determine where it belongs in the list. 1903 */ 1904 search=65536UL; 1905 for (level=p->level; level >= 0; level--) 1906 { 1907 while (p->nodes[search].next[level] < color) 1908 search=p->nodes[search].next[level]; 1909 update[level]=search; 1910 } 1911 /* 1912 Generate a pseudo-random level for this node. 1913 */ 1914 for (level=0; ; level++) 1915 { 1916 pixel_list->seed=(pixel_list->seed*42893621L)+1L; 1917 if ((pixel_list->seed & 0x300) != 0x300) 1918 break; 1919 } 1920 if (level > 8) 1921 level=8; 1922 if (level > (p->level+2)) 1923 level=p->level+2; 1924 /* 1925 If we're raising the list's level, link back to the root node. 1926 */ 1927 while (level > p->level) 1928 { 1929 p->level++; 1930 update[p->level]=65536UL; 1931 } 1932 /* 1933 Link the node into the skip-list. 1934 */ 1935 do 1936 { 1937 p->nodes[color].next[level]=p->nodes[update[level]].next[level]; 1938 p->nodes[update[level]].next[level]=color; 1939 } while (level-- > 0); 1940} 1941 1942static inline void GetMaximumPixelList(PixelList *pixel_list,Quantum *pixel) 1943{ 1944 register SkipList 1945 *p; 1946 1947 size_t 1948 color, 1949 maximum; 1950 1951 ssize_t 1952 count; 1953 1954 /* 1955 Find the maximum value for each of the color. 1956 */ 1957 p=(&pixel_list->skip_list); 1958 color=65536L; 1959 count=0; 1960 maximum=p->nodes[color].next[0]; 1961 do 1962 { 1963 color=p->nodes[color].next[0]; 1964 if (color > maximum) 1965 maximum=color; 1966 count+=p->nodes[color].count; 1967 } while (count < (ssize_t) pixel_list->length); 1968 *pixel=ScaleShortToQuantum((unsigned short) maximum); 1969} 1970 1971static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel) 1972{ 1973 double 1974 sum; 1975 1976 register SkipList 1977 *p; 1978 1979 size_t 1980 color; 1981 1982 ssize_t 1983 count; 1984 1985 /* 1986 Find the mean value for each of the color. 1987 */ 1988 p=(&pixel_list->skip_list); 1989 color=65536L; 1990 count=0; 1991 sum=0.0; 1992 do 1993 { 1994 color=p->nodes[color].next[0]; 1995 sum+=(double) p->nodes[color].count*color; 1996 count+=p->nodes[color].count; 1997 } while (count < (ssize_t) pixel_list->length); 1998 sum/=pixel_list->length; 1999 *pixel=ScaleShortToQuantum((unsigned short) sum); 2000} 2001 2002static inline void GetMedianPixelList(PixelList *pixel_list,Quantum *pixel) 2003{ 2004 register SkipList 2005 *p; 2006 2007 size_t 2008 color; 2009 2010 ssize_t 2011 count; 2012 2013 /* 2014 Find the median value for each of the color. 2015 */ 2016 p=(&pixel_list->skip_list); 2017 color=65536L; 2018 count=0; 2019 do 2020 { 2021 color=p->nodes[color].next[0]; 2022 count+=p->nodes[color].count; 2023 } while (count <= (ssize_t) (pixel_list->length >> 1)); 2024 *pixel=ScaleShortToQuantum((unsigned short) color); 2025} 2026 2027static inline void GetMinimumPixelList(PixelList *pixel_list,Quantum *pixel) 2028{ 2029 register SkipList 2030 *p; 2031 2032 size_t 2033 color, 2034 minimum; 2035 2036 ssize_t 2037 count; 2038 2039 /* 2040 Find the minimum value for each of the color. 2041 */ 2042 p=(&pixel_list->skip_list); 2043 count=0; 2044 color=65536UL; 2045 minimum=p->nodes[color].next[0]; 2046 do 2047 { 2048 color=p->nodes[color].next[0]; 2049 if (color < minimum) 2050 minimum=color; 2051 count+=p->nodes[color].count; 2052 } while (count < (ssize_t) pixel_list->length); 2053 *pixel=ScaleShortToQuantum((unsigned short) minimum); 2054} 2055 2056static inline void GetModePixelList(PixelList *pixel_list,Quantum *pixel) 2057{ 2058 register SkipList 2059 *p; 2060 2061 size_t 2062 color, 2063 max_count, 2064 mode; 2065 2066 ssize_t 2067 count; 2068 2069 /* 2070 Make each pixel the 'predominant color' of the specified neighborhood. 2071 */ 2072 p=(&pixel_list->skip_list); 2073 color=65536L; 2074 mode=color; 2075 max_count=p->nodes[mode].count; 2076 count=0; 2077 do 2078 { 2079 color=p->nodes[color].next[0]; 2080 if (p->nodes[color].count > max_count) 2081 { 2082 mode=color; 2083 max_count=p->nodes[mode].count; 2084 } 2085 count+=p->nodes[color].count; 2086 } while (count < (ssize_t) pixel_list->length); 2087 *pixel=ScaleShortToQuantum((unsigned short) mode); 2088} 2089 2090static inline void GetNonpeakPixelList(PixelList *pixel_list,Quantum *pixel) 2091{ 2092 register SkipList 2093 *p; 2094 2095 size_t 2096 color, 2097 next, 2098 previous; 2099 2100 ssize_t 2101 count; 2102 2103 /* 2104 Finds the non peak value for each of the colors. 2105 */ 2106 p=(&pixel_list->skip_list); 2107 color=65536L; 2108 next=p->nodes[color].next[0]; 2109 count=0; 2110 do 2111 { 2112 previous=color; 2113 color=next; 2114 next=p->nodes[color].next[0]; 2115 count+=p->nodes[color].count; 2116 } while (count <= (ssize_t) (pixel_list->length >> 1)); 2117 if ((previous == 65536UL) && (next != 65536UL)) 2118 color=next; 2119 else 2120 if ((previous != 65536UL) && (next == 65536UL)) 2121 color=previous; 2122 *pixel=ScaleShortToQuantum((unsigned short) color); 2123} 2124 2125static inline void GetStandardDeviationPixelList(PixelList *pixel_list, 2126 Quantum *pixel) 2127{ 2128 double 2129 sum, 2130 sum_squared; 2131 2132 register SkipList 2133 *p; 2134 2135 size_t 2136 color; 2137 2138 ssize_t 2139 count; 2140 2141 /* 2142 Find the standard-deviation value for each of the color. 2143 */ 2144 p=(&pixel_list->skip_list); 2145 color=65536L; 2146 count=0; 2147 sum=0.0; 2148 sum_squared=0.0; 2149 do 2150 { 2151 register ssize_t 2152 i; 2153 2154 color=p->nodes[color].next[0]; 2155 sum+=(double) p->nodes[color].count*color; 2156 for (i=0; i < (ssize_t) p->nodes[color].count; i++) 2157 sum_squared+=((double) color)*((double) color); 2158 count+=p->nodes[color].count; 2159 } while (count < (ssize_t) pixel_list->length); 2160 sum/=pixel_list->length; 2161 sum_squared/=pixel_list->length; 2162 *pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum))); 2163} 2164 2165static inline void InsertPixelList(const Image *image,const Quantum pixel, 2166 PixelList *pixel_list) 2167{ 2168 size_t 2169 signature; 2170 2171 unsigned short 2172 index; 2173 2174 index=ScaleQuantumToShort(pixel); 2175 signature=pixel_list->skip_list.nodes[index].signature; 2176 if (signature == pixel_list->signature) 2177 { 2178 pixel_list->skip_list.nodes[index].count++; 2179 return; 2180 } 2181 AddNodePixelList(pixel_list,index); 2182} 2183 2184static inline double MagickAbsoluteValue(const double x) 2185{ 2186 if (x < 0) 2187 return(-x); 2188 return(x); 2189} 2190 2191static inline size_t MagickMax(const size_t x,const size_t y) 2192{ 2193 if (x > y) 2194 return(x); 2195 return(y); 2196} 2197 2198static void ResetPixelList(PixelList *pixel_list) 2199{ 2200 int 2201 level; 2202 2203 register SkipNode 2204 *root; 2205 2206 register SkipList 2207 *p; 2208 2209 /* 2210 Reset the skip-list. 2211 */ 2212 p=(&pixel_list->skip_list); 2213 root=p->nodes+65536UL; 2214 p->level=0; 2215 for (level=0; level < 9; level++) 2216 root->next[level]=65536UL; 2217 pixel_list->seed=pixel_list->signature++; 2218} 2219 2220MagickExport Image *StatisticImage(const Image *image,const StatisticType type, 2221 const size_t width,const size_t height,ExceptionInfo *exception) 2222{ 2223#define StatisticImageTag "Statistic/Image" 2224 2225 CacheView 2226 *image_view, 2227 *statistic_view; 2228 2229 Image 2230 *statistic_image; 2231 2232 MagickBooleanType 2233 status; 2234 2235 MagickOffsetType 2236 progress; 2237 2238 PixelList 2239 **restrict pixel_list; 2240 2241 ssize_t 2242 center, 2243 y; 2244 2245 /* 2246 Initialize statistics image attributes. 2247 */ 2248 assert(image != (Image *) NULL); 2249 assert(image->signature == MagickSignature); 2250 if (image->debug != MagickFalse) 2251 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2252 assert(exception != (ExceptionInfo *) NULL); 2253 assert(exception->signature == MagickSignature); 2254 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue, 2255 exception); 2256 if (statistic_image == (Image *) NULL) 2257 return((Image *) NULL); 2258 status=SetImageStorageClass(statistic_image,DirectClass,exception); 2259 if (status == MagickFalse) 2260 { 2261 statistic_image=DestroyImage(statistic_image); 2262 return((Image *) NULL); 2263 } 2264 pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1)); 2265 if (pixel_list == (PixelList **) NULL) 2266 { 2267 statistic_image=DestroyImage(statistic_image); 2268 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 2269 } 2270 /* 2271 Make each pixel the min / max / median / mode / etc. of the neighborhood. 2272 */ 2273 center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))* 2274 (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L); 2275 status=MagickTrue; 2276 progress=0; 2277 image_view=AcquireVirtualCacheView(image,exception); 2278 statistic_view=AcquireAuthenticCacheView(statistic_image,exception); 2279#if defined(MAGICKCORE_OPENMP_SUPPORT) 2280 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2281 dynamic_number_threads(image,image->columns,image->rows,1) 2282#endif 2283 for (y=0; y < (ssize_t) statistic_image->rows; y++) 2284 { 2285 const int 2286 id = GetOpenMPThreadId(); 2287 2288 register const Quantum 2289 *restrict p; 2290 2291 register Quantum 2292 *restrict q; 2293 2294 register ssize_t 2295 x; 2296 2297 if (status == MagickFalse) 2298 continue; 2299 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y- 2300 (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1), 2301 MagickMax(height,1),exception); 2302 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception); 2303 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2304 { 2305 status=MagickFalse; 2306 continue; 2307 } 2308 for (x=0; x < (ssize_t) statistic_image->columns; x++) 2309 { 2310 register ssize_t 2311 i; 2312 2313 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 2314 { 2315 PixelChannel 2316 channel; 2317 2318 PixelTrait 2319 statistic_traits, 2320 traits; 2321 2322 Quantum 2323 pixel; 2324 2325 register const Quantum 2326 *restrict pixels; 2327 2328 register ssize_t 2329 u; 2330 2331 ssize_t 2332 v; 2333 2334 channel=GetPixelChannelMapChannel(image,i); 2335 traits=GetPixelChannelMapTraits(image,channel); 2336 statistic_traits=GetPixelChannelMapTraits(statistic_image,channel); 2337 if ((traits == UndefinedPixelTrait) || 2338 (statistic_traits == UndefinedPixelTrait)) 2339 continue; 2340 if (((statistic_traits & CopyPixelTrait) != 0) || 2341 (GetPixelMask(image,p) != 0)) 2342 { 2343 SetPixelChannel(statistic_image,channel,p[center+i],q); 2344 continue; 2345 } 2346 pixels=p; 2347 ResetPixelList(pixel_list[id]); 2348 for (v=0; v < (ssize_t) MagickMax(height,1); v++) 2349 { 2350 for (u=0; u < (ssize_t) MagickMax(width,1); u++) 2351 { 2352 InsertPixelList(image,pixels[i],pixel_list[id]); 2353 pixels+=GetPixelChannels(image); 2354 } 2355 pixels+=image->columns*GetPixelChannels(image); 2356 } 2357 switch (type) 2358 { 2359 case GradientStatistic: 2360 { 2361 double 2362 maximum, 2363 minimum; 2364 2365 GetMinimumPixelList(pixel_list[id],&pixel); 2366 minimum=(double) pixel; 2367 GetMaximumPixelList(pixel_list[id],&pixel); 2368 maximum=(double) pixel; 2369 pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum)); 2370 break; 2371 } 2372 case MaximumStatistic: 2373 { 2374 GetMaximumPixelList(pixel_list[id],&pixel); 2375 break; 2376 } 2377 case MeanStatistic: 2378 { 2379 GetMeanPixelList(pixel_list[id],&pixel); 2380 break; 2381 } 2382 case MedianStatistic: 2383 default: 2384 { 2385 GetMedianPixelList(pixel_list[id],&pixel); 2386 break; 2387 } 2388 case MinimumStatistic: 2389 { 2390 GetMinimumPixelList(pixel_list[id],&pixel); 2391 break; 2392 } 2393 case ModeStatistic: 2394 { 2395 GetModePixelList(pixel_list[id],&pixel); 2396 break; 2397 } 2398 case NonpeakStatistic: 2399 { 2400 GetNonpeakPixelList(pixel_list[id],&pixel); 2401 break; 2402 } 2403 case StandardDeviationStatistic: 2404 { 2405 GetStandardDeviationPixelList(pixel_list[id],&pixel); 2406 break; 2407 } 2408 } 2409 SetPixelChannel(statistic_image,channel,pixel,q); 2410 } 2411 p+=GetPixelChannels(image); 2412 q+=GetPixelChannels(statistic_image); 2413 } 2414 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse) 2415 status=MagickFalse; 2416 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2417 { 2418 MagickBooleanType 2419 proceed; 2420 2421#if defined(MAGICKCORE_OPENMP_SUPPORT) 2422 #pragma omp critical (MagickCore_StatisticImage) 2423#endif 2424 proceed=SetImageProgress(image,StatisticImageTag,progress++, 2425 image->rows); 2426 if (proceed == MagickFalse) 2427 status=MagickFalse; 2428 } 2429 } 2430 statistic_view=DestroyCacheView(statistic_view); 2431 image_view=DestroyCacheView(image_view); 2432 pixel_list=DestroyPixelListThreadSet(pixel_list); 2433 return(statistic_image); 2434} 2435