1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % EEEEE N N H H AAA N N CCCC EEEEE % 7 % E NN N H H A A NN N C E % 8 % EEE N N N HHHHH AAAAA N N N C EEE % 9 % E N NN H H A A N NN C E % 10 % EEEEE N N H H A A N N CCCC EEEEE % 11 % % 12 % % 13 % MagickCore Image Enhancement Methods % 14 % % 15 % Software Design % 16 % Cristy % 17 % July 1992 % 18 % % 19 % % 20 % Copyright 1999-2016 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 /* 42 Include declarations. 43 */ 44 #include "MagickCore/studio.h" 45 #include "MagickCore/accelerate-private.h" 46 #include "MagickCore/artifact.h" 47 #include "MagickCore/attribute.h" 48 #include "MagickCore/cache.h" 49 #include "MagickCore/cache-view.h" 50 #include "MagickCore/channel.h" 51 #include "MagickCore/color.h" 52 #include "MagickCore/color-private.h" 53 #include "MagickCore/colorspace.h" 54 #include "MagickCore/colorspace-private.h" 55 #include "MagickCore/composite-private.h" 56 #include "MagickCore/enhance.h" 57 #include "MagickCore/exception.h" 58 #include "MagickCore/exception-private.h" 59 #include "MagickCore/fx.h" 60 #include "MagickCore/gem.h" 61 #include "MagickCore/gem-private.h" 62 #include "MagickCore/geometry.h" 63 #include "MagickCore/histogram.h" 64 #include "MagickCore/image.h" 65 #include "MagickCore/image-private.h" 66 #include "MagickCore/memory_.h" 67 #include "MagickCore/monitor.h" 68 #include "MagickCore/monitor-private.h" 69 #include "MagickCore/option.h" 70 #include "MagickCore/pixel.h" 71 #include "MagickCore/pixel-accessor.h" 72 #include "MagickCore/quantum.h" 73 #include "MagickCore/quantum-private.h" 74 #include "MagickCore/resample.h" 75 #include "MagickCore/resample-private.h" 76 #include "MagickCore/resource_.h" 77 #include "MagickCore/statistic.h" 78 #include "MagickCore/string_.h" 79 #include "MagickCore/string-private.h" 80 #include "MagickCore/thread-private.h" 81 #include "MagickCore/threshold.h" 82 #include "MagickCore/token.h" 83 #include "MagickCore/xml-tree.h" 84 #include "MagickCore/xml-tree-private.h" 85 86 /* 88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 89 % % 90 % % 91 % % 92 % A u t o G a m m a I m a g e % 93 % % 94 % % 95 % % 96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 97 % 98 % AutoGammaImage() extract the 'mean' from the image and adjust the image 99 % to try make set its gamma appropriatally. 100 % 101 % The format of the AutoGammaImage method is: 102 % 103 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception) 104 % 105 % A description of each parameter follows: 106 % 107 % o image: The image to auto-level 108 % 109 % o exception: return any errors or warnings in this structure. 110 % 111 */ 112 MagickExport MagickBooleanType AutoGammaImage(Image *image, 113 ExceptionInfo *exception) 114 { 115 double 116 gamma, 117 log_mean, 118 mean, 119 sans; 120 121 MagickStatusType 122 status; 123 124 register ssize_t 125 i; 126 127 log_mean=log(0.5); 128 if (image->channel_mask == DefaultChannels) 129 { 130 /* 131 Apply gamma correction equally across all given channels. 132 */ 133 (void) GetImageMean(image,&mean,&sans,exception); 134 gamma=log(mean*QuantumScale)/log_mean; 135 return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception)); 136 } 137 /* 138 Auto-gamma each channel separately. 139 */ 140 status=MagickTrue; 141 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 142 { 143 ChannelType 144 channel_mask; 145 146 PixelChannel channel=GetPixelChannelChannel(image,i); 147 PixelTrait traits=GetPixelChannelTraits(image,channel); 148 if ((traits & UpdatePixelTrait) == 0) 149 continue; 150 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i)); 151 status=GetImageMean(image,&mean,&sans,exception); 152 gamma=log(mean*QuantumScale)/log_mean; 153 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception); 154 (void) SetImageChannelMask(image,channel_mask); 155 if (status == MagickFalse) 156 break; 157 } 158 return(status != 0 ? MagickTrue : MagickFalse); 159 } 160 161 /* 163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 164 % % 165 % % 166 % % 167 % A u t o L e v e l I m a g e % 168 % % 169 % % 170 % % 171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 172 % 173 % AutoLevelImage() adjusts the levels of a particular image channel by 174 % scaling the minimum and maximum values to the full quantum range. 175 % 176 % The format of the LevelImage method is: 177 % 178 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception) 179 % 180 % A description of each parameter follows: 181 % 182 % o image: The image to auto-level 183 % 184 % o exception: return any errors or warnings in this structure. 185 % 186 */ 187 MagickExport MagickBooleanType AutoLevelImage(Image *image, 188 ExceptionInfo *exception) 189 { 190 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception)); 191 } 192 193 /* 195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 196 % % 197 % % 198 % % 199 % B r i g h t n e s s C o n t r a s t I m a g e % 200 % % 201 % % 202 % % 203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 204 % 205 % BrightnessContrastImage() changes the brightness and/or contrast of an 206 % image. It converts the brightness and contrast parameters into slope and 207 % intercept and calls a polynomical function to apply to the image. 208 % 209 % The format of the BrightnessContrastImage method is: 210 % 211 % MagickBooleanType BrightnessContrastImage(Image *image, 212 % const double brightness,const double contrast,ExceptionInfo *exception) 213 % 214 % A description of each parameter follows: 215 % 216 % o image: the image. 217 % 218 % o brightness: the brightness percent (-100 .. 100). 219 % 220 % o contrast: the contrast percent (-100 .. 100). 221 % 222 % o exception: return any errors or warnings in this structure. 223 % 224 */ 225 MagickExport MagickBooleanType BrightnessContrastImage(Image *image, 226 const double brightness,const double contrast,ExceptionInfo *exception) 227 { 228 #define BrightnessContastImageTag "BrightnessContast/Image" 229 230 double 231 alpha, 232 coefficients[2], 233 intercept, 234 slope; 235 236 MagickBooleanType 237 status; 238 239 /* 240 Compute slope and intercept. 241 */ 242 assert(image != (Image *) NULL); 243 assert(image->signature == MagickCoreSignature); 244 if (image->debug != MagickFalse) 245 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 246 alpha=contrast; 247 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0)); 248 if (slope < 0.0) 249 slope=0.0; 250 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope); 251 coefficients[0]=slope; 252 coefficients[1]=intercept; 253 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception); 254 return(status); 255 } 256 257 /* 259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 260 % % 261 % % 262 % % 263 % C l u t I m a g e % 264 % % 265 % % 266 % % 267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 268 % 269 % ClutImage() replaces each color value in the given image, by using it as an 270 % index to lookup a replacement color value in a Color Look UP Table in the 271 % form of an image. The values are extracted along a diagonal of the CLUT 272 % image so either a horizontal or vertial gradient image can be used. 273 % 274 % Typically this is used to either re-color a gray-scale image according to a 275 % color gradient in the CLUT image, or to perform a freeform histogram 276 % (level) adjustment according to the (typically gray-scale) gradient in the 277 % CLUT image. 278 % 279 % When the 'channel' mask includes the matte/alpha transparency channel but 280 % one image has no such channel it is assumed that that image is a simple 281 % gray-scale image that will effect the alpha channel values, either for 282 % gray-scale coloring (with transparent or semi-transparent colors), or 283 % a histogram adjustment of existing alpha channel values. If both images 284 % have matte channels, direct and normal indexing is applied, which is rarely 285 % used. 286 % 287 % The format of the ClutImage method is: 288 % 289 % MagickBooleanType ClutImage(Image *image,Image *clut_image, 290 % const PixelInterpolateMethod method,ExceptionInfo *exception) 291 % 292 % A description of each parameter follows: 293 % 294 % o image: the image, which is replaced by indexed CLUT values 295 % 296 % o clut_image: the color lookup table image for replacement color values. 297 % 298 % o method: the pixel interpolation method. 299 % 300 % o exception: return any errors or warnings in this structure. 301 % 302 */ 303 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image, 304 const PixelInterpolateMethod method,ExceptionInfo *exception) 305 { 306 #define ClutImageTag "Clut/Image" 307 308 CacheView 309 *clut_view, 310 *image_view; 311 312 MagickBooleanType 313 status; 314 315 MagickOffsetType 316 progress; 317 318 PixelInfo 319 *clut_map; 320 321 register ssize_t 322 i; 323 324 ssize_t adjust, 325 y; 326 327 assert(image != (Image *) NULL); 328 assert(image->signature == MagickCoreSignature); 329 if (image->debug != MagickFalse) 330 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 331 assert(clut_image != (Image *) NULL); 332 assert(clut_image->signature == MagickCoreSignature); 333 if( SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 334 return(MagickFalse); 335 if( (IsGrayColorspace(image->colorspace) != MagickFalse) && 336 (IsGrayColorspace(clut_image->colorspace) == MagickFalse)) 337 (void) SetImageColorspace(image,sRGBColorspace,exception); 338 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map)); 339 if (clut_map == (PixelInfo *) NULL) 340 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 341 image->filename); 342 /* 343 Clut image. 344 */ 345 status=MagickTrue; 346 progress=0; 347 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1); 348 clut_view=AcquireVirtualCacheView(clut_image,exception); 349 for (i=0; i <= (ssize_t) MaxMap; i++) 350 { 351 GetPixelInfo(clut_image,clut_map+i); 352 (void) InterpolatePixelInfo(clut_image,clut_view,method, 353 (double) i*(clut_image->columns-adjust)/MaxMap,(double) i* 354 (clut_image->rows-adjust)/MaxMap,clut_map+i,exception); 355 } 356 clut_view=DestroyCacheView(clut_view); 357 image_view=AcquireAuthenticCacheView(image,exception); 358 #if defined(MAGICKCORE_OPENMP_SUPPORT) 359 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 360 magick_threads(image,image,image->rows,1) 361 #endif 362 for (y=0; y < (ssize_t) image->rows; y++) 363 { 364 PixelInfo 365 pixel; 366 367 register Quantum 368 *magick_restrict q; 369 370 register ssize_t 371 x; 372 373 if (status == MagickFalse) 374 continue; 375 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 376 if (q == (Quantum *) NULL) 377 { 378 status=MagickFalse; 379 continue; 380 } 381 GetPixelInfo(image,&pixel); 382 for (x=0; x < (ssize_t) image->columns; x++) 383 { 384 PixelTrait 385 traits; 386 387 if (GetPixelReadMask(image,q) == 0) 388 { 389 q+=GetPixelChannels(image); 390 continue; 391 } 392 GetPixelInfoPixel(image,q,&pixel); 393 traits=GetPixelChannelTraits(image,RedPixelChannel); 394 if ((traits & UpdatePixelTrait) != 0) 395 pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum( 396 pixel.red))].red; 397 traits=GetPixelChannelTraits(image,GreenPixelChannel); 398 if ((traits & UpdatePixelTrait) != 0) 399 pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum( 400 pixel.green))].green; 401 traits=GetPixelChannelTraits(image,BluePixelChannel); 402 if ((traits & UpdatePixelTrait) != 0) 403 pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum( 404 pixel.blue))].blue; 405 traits=GetPixelChannelTraits(image,BlackPixelChannel); 406 if ((traits & UpdatePixelTrait) != 0) 407 pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum( 408 pixel.black))].black; 409 traits=GetPixelChannelTraits(image,AlphaPixelChannel); 410 if ((traits & UpdatePixelTrait) != 0) 411 pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum( 412 pixel.alpha))].alpha; 413 SetPixelViaPixelInfo(image,&pixel,q); 414 q+=GetPixelChannels(image); 415 } 416 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 417 status=MagickFalse; 418 if (image->progress_monitor != (MagickProgressMonitor) NULL) 419 { 420 MagickBooleanType 421 proceed; 422 423 #if defined(MAGICKCORE_OPENMP_SUPPORT) 424 #pragma omp critical (MagickCore_ClutImage) 425 #endif 426 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows); 427 if (proceed == MagickFalse) 428 status=MagickFalse; 429 } 430 } 431 image_view=DestroyCacheView(image_view); 432 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map); 433 if ((clut_image->alpha_trait != UndefinedPixelTrait) && 434 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)) 435 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception); 436 return(status); 437 } 438 439 /* 441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 442 % % 443 % % 444 % % 445 % C o l o r D e c i s i o n L i s t I m a g e % 446 % % 447 % % 448 % % 449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 450 % 451 % ColorDecisionListImage() accepts a lightweight Color Correction Collection 452 % (CCC) file which solely contains one or more color corrections and applies 453 % the correction to the image. Here is a sample CCC file: 454 % 455 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2"> 456 % <ColorCorrection id="cc03345"> 457 % <SOPNode> 458 % <Slope> 0.9 1.2 0.5 </Slope> 459 % <Offset> 0.4 -0.5 0.6 </Offset> 460 % <Power> 1.0 0.8 1.5 </Power> 461 % </SOPNode> 462 % <SATNode> 463 % <Saturation> 0.85 </Saturation> 464 % </SATNode> 465 % </ColorCorrection> 466 % </ColorCorrectionCollection> 467 % 468 % which includes the slop, offset, and power for each of the RGB channels 469 % as well as the saturation. 470 % 471 % The format of the ColorDecisionListImage method is: 472 % 473 % MagickBooleanType ColorDecisionListImage(Image *image, 474 % const char *color_correction_collection,ExceptionInfo *exception) 475 % 476 % A description of each parameter follows: 477 % 478 % o image: the image. 479 % 480 % o color_correction_collection: the color correction collection in XML. 481 % 482 % o exception: return any errors or warnings in this structure. 483 % 484 */ 485 MagickExport MagickBooleanType ColorDecisionListImage(Image *image, 486 const char *color_correction_collection,ExceptionInfo *exception) 487 { 488 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image" 489 490 typedef struct _Correction 491 { 492 double 493 slope, 494 offset, 495 power; 496 } Correction; 497 498 typedef struct _ColorCorrection 499 { 500 Correction 501 red, 502 green, 503 blue; 504 505 double 506 saturation; 507 } ColorCorrection; 508 509 CacheView 510 *image_view; 511 512 char 513 token[MagickPathExtent]; 514 515 ColorCorrection 516 color_correction; 517 518 const char 519 *content, 520 *p; 521 522 MagickBooleanType 523 status; 524 525 MagickOffsetType 526 progress; 527 528 PixelInfo 529 *cdl_map; 530 531 register ssize_t 532 i; 533 534 ssize_t 535 y; 536 537 XMLTreeInfo 538 *cc, 539 *ccc, 540 *sat, 541 *sop; 542 543 /* 544 Allocate and initialize cdl maps. 545 */ 546 assert(image != (Image *) NULL); 547 assert(image->signature == MagickCoreSignature); 548 if (image->debug != MagickFalse) 549 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 550 if (color_correction_collection == (const char *) NULL) 551 return(MagickFalse); 552 ccc=NewXMLTree((const char *) color_correction_collection,exception); 553 if (ccc == (XMLTreeInfo *) NULL) 554 return(MagickFalse); 555 cc=GetXMLTreeChild(ccc,"ColorCorrection"); 556 if (cc == (XMLTreeInfo *) NULL) 557 { 558 ccc=DestroyXMLTree(ccc); 559 return(MagickFalse); 560 } 561 color_correction.red.slope=1.0; 562 color_correction.red.offset=0.0; 563 color_correction.red.power=1.0; 564 color_correction.green.slope=1.0; 565 color_correction.green.offset=0.0; 566 color_correction.green.power=1.0; 567 color_correction.blue.slope=1.0; 568 color_correction.blue.offset=0.0; 569 color_correction.blue.power=1.0; 570 color_correction.saturation=0.0; 571 sop=GetXMLTreeChild(cc,"SOPNode"); 572 if (sop != (XMLTreeInfo *) NULL) 573 { 574 XMLTreeInfo 575 *offset, 576 *power, 577 *slope; 578 579 slope=GetXMLTreeChild(sop,"Slope"); 580 if (slope != (XMLTreeInfo *) NULL) 581 { 582 content=GetXMLTreeContent(slope); 583 p=(const char *) content; 584 for (i=0; (*p != '\0') && (i < 3); i++) 585 { 586 GetNextToken(p,&p,MagickPathExtent,token); 587 if (*token == ',') 588 GetNextToken(p,&p,MagickPathExtent,token); 589 switch (i) 590 { 591 case 0: 592 { 593 color_correction.red.slope=StringToDouble(token,(char **) NULL); 594 break; 595 } 596 case 1: 597 { 598 color_correction.green.slope=StringToDouble(token, 599 (char **) NULL); 600 break; 601 } 602 case 2: 603 { 604 color_correction.blue.slope=StringToDouble(token, 605 (char **) NULL); 606 break; 607 } 608 } 609 } 610 } 611 offset=GetXMLTreeChild(sop,"Offset"); 612 if (offset != (XMLTreeInfo *) NULL) 613 { 614 content=GetXMLTreeContent(offset); 615 p=(const char *) content; 616 for (i=0; (*p != '\0') && (i < 3); i++) 617 { 618 GetNextToken(p,&p,MagickPathExtent,token); 619 if (*token == ',') 620 GetNextToken(p,&p,MagickPathExtent,token); 621 switch (i) 622 { 623 case 0: 624 { 625 color_correction.red.offset=StringToDouble(token, 626 (char **) NULL); 627 break; 628 } 629 case 1: 630 { 631 color_correction.green.offset=StringToDouble(token, 632 (char **) NULL); 633 break; 634 } 635 case 2: 636 { 637 color_correction.blue.offset=StringToDouble(token, 638 (char **) NULL); 639 break; 640 } 641 } 642 } 643 } 644 power=GetXMLTreeChild(sop,"Power"); 645 if (power != (XMLTreeInfo *) NULL) 646 { 647 content=GetXMLTreeContent(power); 648 p=(const char *) content; 649 for (i=0; (*p != '\0') && (i < 3); i++) 650 { 651 GetNextToken(p,&p,MagickPathExtent,token); 652 if (*token == ',') 653 GetNextToken(p,&p,MagickPathExtent,token); 654 switch (i) 655 { 656 case 0: 657 { 658 color_correction.red.power=StringToDouble(token,(char **) NULL); 659 break; 660 } 661 case 1: 662 { 663 color_correction.green.power=StringToDouble(token, 664 (char **) NULL); 665 break; 666 } 667 case 2: 668 { 669 color_correction.blue.power=StringToDouble(token, 670 (char **) NULL); 671 break; 672 } 673 } 674 } 675 } 676 } 677 sat=GetXMLTreeChild(cc,"SATNode"); 678 if (sat != (XMLTreeInfo *) NULL) 679 { 680 XMLTreeInfo 681 *saturation; 682 683 saturation=GetXMLTreeChild(sat,"Saturation"); 684 if (saturation != (XMLTreeInfo *) NULL) 685 { 686 content=GetXMLTreeContent(saturation); 687 p=(const char *) content; 688 GetNextToken(p,&p,MagickPathExtent,token); 689 color_correction.saturation=StringToDouble(token,(char **) NULL); 690 } 691 } 692 ccc=DestroyXMLTree(ccc); 693 if (image->debug != MagickFalse) 694 { 695 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 696 " Color Correction Collection:"); 697 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 698 " color_correction.red.slope: %g",color_correction.red.slope); 699 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 700 " color_correction.red.offset: %g",color_correction.red.offset); 701 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 702 " color_correction.red.power: %g",color_correction.red.power); 703 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 704 " color_correction.green.slope: %g",color_correction.green.slope); 705 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 706 " color_correction.green.offset: %g",color_correction.green.offset); 707 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 708 " color_correction.green.power: %g",color_correction.green.power); 709 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 710 " color_correction.blue.slope: %g",color_correction.blue.slope); 711 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 712 " color_correction.blue.offset: %g",color_correction.blue.offset); 713 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 714 " color_correction.blue.power: %g",color_correction.blue.power); 715 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 716 " color_correction.saturation: %g",color_correction.saturation); 717 } 718 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map)); 719 if (cdl_map == (PixelInfo *) NULL) 720 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 721 image->filename); 722 for (i=0; i <= (ssize_t) MaxMap; i++) 723 { 724 cdl_map[i].red=(double) ScaleMapToQuantum((double) 725 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+ 726 color_correction.red.offset,color_correction.red.power)))); 727 cdl_map[i].green=(double) ScaleMapToQuantum((double) 728 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+ 729 color_correction.green.offset,color_correction.green.power)))); 730 cdl_map[i].blue=(double) ScaleMapToQuantum((double) 731 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+ 732 color_correction.blue.offset,color_correction.blue.power)))); 733 } 734 if (image->storage_class == PseudoClass) 735 for (i=0; i < (ssize_t) image->colors; i++) 736 { 737 /* 738 Apply transfer function to colormap. 739 */ 740 double 741 luma; 742 743 luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+ 744 0.07217f*image->colormap[i].blue; 745 image->colormap[i].red=luma+color_correction.saturation*cdl_map[ 746 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma; 747 image->colormap[i].green=luma+color_correction.saturation*cdl_map[ 748 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma; 749 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[ 750 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma; 751 } 752 /* 753 Apply transfer function to image. 754 */ 755 status=MagickTrue; 756 progress=0; 757 image_view=AcquireAuthenticCacheView(image,exception); 758 #if defined(MAGICKCORE_OPENMP_SUPPORT) 759 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 760 magick_threads(image,image,image->rows,1) 761 #endif 762 for (y=0; y < (ssize_t) image->rows; y++) 763 { 764 double 765 luma; 766 767 register Quantum 768 *magick_restrict q; 769 770 register ssize_t 771 x; 772 773 if (status == MagickFalse) 774 continue; 775 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 776 if (q == (Quantum *) NULL) 777 { 778 status=MagickFalse; 779 continue; 780 } 781 for (x=0; x < (ssize_t) image->columns; x++) 782 { 783 luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+ 784 0.07217f*GetPixelBlue(image,q); 785 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation* 786 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q); 787 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation* 788 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q); 789 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation* 790 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q); 791 q+=GetPixelChannels(image); 792 } 793 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 794 status=MagickFalse; 795 if (image->progress_monitor != (MagickProgressMonitor) NULL) 796 { 797 MagickBooleanType 798 proceed; 799 800 #if defined(MAGICKCORE_OPENMP_SUPPORT) 801 #pragma omp critical (MagickCore_ColorDecisionListImageChannel) 802 #endif 803 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag, 804 progress++,image->rows); 805 if (proceed == MagickFalse) 806 status=MagickFalse; 807 } 808 } 809 image_view=DestroyCacheView(image_view); 810 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map); 811 return(status); 812 } 813 814 /* 816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 817 % % 818 % % 819 % % 820 % C o n t r a s t I m a g e % 821 % % 822 % % 823 % % 824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 825 % 826 % ContrastImage() enhances the intensity differences between the lighter and 827 % darker elements of the image. Set sharpen to a MagickTrue to increase the 828 % image contrast otherwise the contrast is reduced. 829 % 830 % The format of the ContrastImage method is: 831 % 832 % MagickBooleanType ContrastImage(Image *image, 833 % const MagickBooleanType sharpen,ExceptionInfo *exception) 834 % 835 % A description of each parameter follows: 836 % 837 % o image: the image. 838 % 839 % o sharpen: Increase or decrease image contrast. 840 % 841 % o exception: return any errors or warnings in this structure. 842 % 843 */ 844 845 static void Contrast(const int sign,double *red,double *green,double *blue) 846 { 847 double 848 brightness, 849 hue, 850 saturation; 851 852 /* 853 Enhance contrast: dark color become darker, light color become lighter. 854 */ 855 assert(red != (double *) NULL); 856 assert(green != (double *) NULL); 857 assert(blue != (double *) NULL); 858 hue=0.0; 859 saturation=0.0; 860 brightness=0.0; 861 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness); 862 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)- 863 brightness); 864 if (brightness > 1.0) 865 brightness=1.0; 866 else 867 if (brightness < 0.0) 868 brightness=0.0; 869 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue); 870 } 871 872 MagickExport MagickBooleanType ContrastImage(Image *image, 873 const MagickBooleanType sharpen,ExceptionInfo *exception) 874 { 875 #define ContrastImageTag "Contrast/Image" 876 877 CacheView 878 *image_view; 879 880 int 881 sign; 882 883 MagickBooleanType 884 status; 885 886 MagickOffsetType 887 progress; 888 889 register ssize_t 890 i; 891 892 ssize_t 893 y; 894 895 assert(image != (Image *) NULL); 896 assert(image->signature == MagickCoreSignature); 897 #if defined(MAGICKCORE_OPENCL_SUPPORT) 898 if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse) 899 return(MagickTrue); 900 #endif 901 if (image->debug != MagickFalse) 902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 903 sign=sharpen != MagickFalse ? 1 : -1; 904 if (image->storage_class == PseudoClass) 905 { 906 /* 907 Contrast enhance colormap. 908 */ 909 for (i=0; i < (ssize_t) image->colors; i++) 910 { 911 double 912 blue, 913 green, 914 red; 915 916 red=(double) image->colormap[i].red; 917 green=(double) image->colormap[i].green; 918 blue=(double) image->colormap[i].blue; 919 Contrast(sign,&red,&green,&blue); 920 image->colormap[i].red=(MagickRealType) red; 921 image->colormap[i].green=(MagickRealType) green; 922 image->colormap[i].blue=(MagickRealType) blue; 923 } 924 } 925 /* 926 Contrast enhance image. 927 */ 928 status=MagickTrue; 929 progress=0; 930 image_view=AcquireAuthenticCacheView(image,exception); 931 #if defined(MAGICKCORE_OPENMP_SUPPORT) 932 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 933 magick_threads(image,image,image->rows,1) 934 #endif 935 for (y=0; y < (ssize_t) image->rows; y++) 936 { 937 double 938 blue, 939 green, 940 red; 941 942 register Quantum 943 *magick_restrict q; 944 945 register ssize_t 946 x; 947 948 if (status == MagickFalse) 949 continue; 950 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 951 if (q == (Quantum *) NULL) 952 { 953 status=MagickFalse; 954 continue; 955 } 956 for (x=0; x < (ssize_t) image->columns; x++) 957 { 958 red=(double) GetPixelRed(image,q); 959 green=(double) GetPixelGreen(image,q); 960 blue=(double) GetPixelBlue(image,q); 961 Contrast(sign,&red,&green,&blue); 962 SetPixelRed(image,ClampToQuantum(red),q); 963 SetPixelGreen(image,ClampToQuantum(green),q); 964 SetPixelBlue(image,ClampToQuantum(blue),q); 965 q+=GetPixelChannels(image); 966 } 967 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 968 status=MagickFalse; 969 if (image->progress_monitor != (MagickProgressMonitor) NULL) 970 { 971 MagickBooleanType 972 proceed; 973 974 #if defined(MAGICKCORE_OPENMP_SUPPORT) 975 #pragma omp critical (MagickCore_ContrastImage) 976 #endif 977 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows); 978 if (proceed == MagickFalse) 979 status=MagickFalse; 980 } 981 } 982 image_view=DestroyCacheView(image_view); 983 return(status); 984 } 985 986 /* 988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 989 % % 990 % % 991 % % 992 % C o n t r a s t S t r e t c h I m a g e % 993 % % 994 % % 995 % % 996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 997 % 998 % ContrastStretchImage() is a simple image enhancement technique that attempts 999 % to improve the contrast in an image by 'stretching' the range of intensity 1000 % values it contains to span a desired range of values. It differs from the 1001 % more sophisticated histogram equalization in that it can only apply a 1002 % linear scaling function to the image pixel values. As a result the 1003 % 'enhancement' is less harsh. 1004 % 1005 % The format of the ContrastStretchImage method is: 1006 % 1007 % MagickBooleanType ContrastStretchImage(Image *image, 1008 % const char *levels,ExceptionInfo *exception) 1009 % 1010 % A description of each parameter follows: 1011 % 1012 % o image: the image. 1013 % 1014 % o black_point: the black point. 1015 % 1016 % o white_point: the white point. 1017 % 1018 % o levels: Specify the levels where the black and white points have the 1019 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.). 1020 % 1021 % o exception: return any errors or warnings in this structure. 1022 % 1023 */ 1024 MagickExport MagickBooleanType ContrastStretchImage(Image *image, 1025 const double black_point,const double white_point,ExceptionInfo *exception) 1026 { 1027 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color))) 1028 #define ContrastStretchImageTag "ContrastStretch/Image" 1029 1030 CacheView 1031 *image_view; 1032 1033 double 1034 *black, 1035 *histogram, 1036 *stretch_map, 1037 *white; 1038 1039 MagickBooleanType 1040 status; 1041 1042 MagickOffsetType 1043 progress; 1044 1045 register ssize_t 1046 i; 1047 1048 ssize_t 1049 y; 1050 1051 /* 1052 Allocate histogram and stretch map. 1053 */ 1054 assert(image != (Image *) NULL); 1055 assert(image->signature == MagickCoreSignature); 1056 if (image->debug != MagickFalse) 1057 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1058 if (SetImageGray(image,exception) != MagickFalse) 1059 (void) SetImageColorspace(image,GRAYColorspace,exception); 1060 black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black)); 1061 white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white)); 1062 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)* 1063 sizeof(*histogram)); 1064 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL, 1065 GetPixelChannels(image)*sizeof(*stretch_map)); 1066 if ((black == (double *) NULL) || (white == (double *) NULL) || 1067 (histogram == (double *) NULL) || (stretch_map == (double *) NULL)) 1068 { 1069 if (stretch_map != (double *) NULL) 1070 stretch_map=(double *) RelinquishMagickMemory(stretch_map); 1071 if (histogram != (double *) NULL) 1072 histogram=(double *) RelinquishMagickMemory(histogram); 1073 if (white != (double *) NULL) 1074 white=(double *) RelinquishMagickMemory(white); 1075 if (black != (double *) NULL) 1076 black=(double *) RelinquishMagickMemory(black); 1077 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 1078 image->filename); 1079 } 1080 /* 1081 Form histogram. 1082 */ 1083 status=MagickTrue; 1084 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)* 1085 sizeof(*histogram)); 1086 image_view=AcquireVirtualCacheView(image,exception); 1087 for (y=0; y < (ssize_t) image->rows; y++) 1088 { 1089 register const Quantum 1090 *magick_restrict p; 1091 1092 register ssize_t 1093 x; 1094 1095 if (status == MagickFalse) 1096 continue; 1097 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1098 if (p == (const Quantum *) NULL) 1099 { 1100 status=MagickFalse; 1101 continue; 1102 } 1103 for (x=0; x < (ssize_t) image->columns; x++) 1104 { 1105 double 1106 pixel; 1107 1108 pixel=GetPixelIntensity(image,p); 1109 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1110 { 1111 if (image->channel_mask != DefaultChannels) 1112 pixel=(double) p[i]; 1113 histogram[GetPixelChannels(image)*ScaleQuantumToMap( 1114 ClampToQuantum(pixel))+i]++; 1115 } 1116 p+=GetPixelChannels(image); 1117 } 1118 } 1119 image_view=DestroyCacheView(image_view); 1120 /* 1121 Find the histogram boundaries by locating the black/white levels. 1122 */ 1123 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1124 { 1125 double 1126 intensity; 1127 1128 register ssize_t 1129 j; 1130 1131 black[i]=0.0; 1132 white[i]=MaxRange(QuantumRange); 1133 intensity=0.0; 1134 for (j=0; j <= (ssize_t) MaxMap; j++) 1135 { 1136 intensity+=histogram[GetPixelChannels(image)*j+i]; 1137 if (intensity > black_point) 1138 break; 1139 } 1140 black[i]=(double) j; 1141 intensity=0.0; 1142 for (j=(ssize_t) MaxMap; j != 0; j--) 1143 { 1144 intensity+=histogram[GetPixelChannels(image)*j+i]; 1145 if (intensity > ((double) image->columns*image->rows-white_point)) 1146 break; 1147 } 1148 white[i]=(double) j; 1149 } 1150 histogram=(double *) RelinquishMagickMemory(histogram); 1151 /* 1152 Stretch the histogram to create the stretched image mapping. 1153 */ 1154 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)* 1155 sizeof(*stretch_map)); 1156 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1157 { 1158 register ssize_t 1159 j; 1160 1161 for (j=0; j <= (ssize_t) MaxMap; j++) 1162 { 1163 double 1164 gamma; 1165 1166 gamma=PerceptibleReciprocal(white[i]-black[i]); 1167 if (j < (ssize_t) black[i]) 1168 stretch_map[GetPixelChannels(image)*j+i]=0.0; 1169 else 1170 if (j > (ssize_t) white[i]) 1171 stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange; 1172 else 1173 stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum( 1174 (double) (MaxMap*gamma*(j-black[i]))); 1175 } 1176 } 1177 if (image->storage_class == PseudoClass) 1178 { 1179 register ssize_t 1180 j; 1181 1182 /* 1183 Stretch-contrast colormap. 1184 */ 1185 for (j=0; j < (ssize_t) image->colors; j++) 1186 { 1187 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 1188 { 1189 i=GetPixelChannelOffset(image,RedPixelChannel); 1190 image->colormap[j].red=stretch_map[GetPixelChannels(image)* 1191 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i]; 1192 } 1193 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 1194 { 1195 i=GetPixelChannelOffset(image,GreenPixelChannel); 1196 image->colormap[j].green=stretch_map[GetPixelChannels(image)* 1197 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i]; 1198 } 1199 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 1200 { 1201 i=GetPixelChannelOffset(image,BluePixelChannel); 1202 image->colormap[j].blue=stretch_map[GetPixelChannels(image)* 1203 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i]; 1204 } 1205 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 1206 { 1207 i=GetPixelChannelOffset(image,AlphaPixelChannel); 1208 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)* 1209 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i]; 1210 } 1211 } 1212 } 1213 /* 1214 Stretch-contrast image. 1215 */ 1216 status=MagickTrue; 1217 progress=0; 1218 image_view=AcquireAuthenticCacheView(image,exception); 1219 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1220 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1221 magick_threads(image,image,image->rows,1) 1222 #endif 1223 for (y=0; y < (ssize_t) image->rows; y++) 1224 { 1225 register Quantum 1226 *magick_restrict q; 1227 1228 register ssize_t 1229 x; 1230 1231 if (status == MagickFalse) 1232 continue; 1233 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 1234 if (q == (Quantum *) NULL) 1235 { 1236 status=MagickFalse; 1237 continue; 1238 } 1239 for (x=0; x < (ssize_t) image->columns; x++) 1240 { 1241 register ssize_t 1242 j; 1243 1244 if (GetPixelReadMask(image,q) == 0) 1245 { 1246 q+=GetPixelChannels(image); 1247 continue; 1248 } 1249 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 1250 { 1251 PixelChannel channel=GetPixelChannelChannel(image,j); 1252 PixelTrait traits=GetPixelChannelTraits(image,channel); 1253 if ((traits & UpdatePixelTrait) == 0) 1254 continue; 1255 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)* 1256 ScaleQuantumToMap(q[j])+j]); 1257 } 1258 q+=GetPixelChannels(image); 1259 } 1260 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1261 status=MagickFalse; 1262 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1263 { 1264 MagickBooleanType 1265 proceed; 1266 1267 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1268 #pragma omp critical (MagickCore_ContrastStretchImage) 1269 #endif 1270 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++, 1271 image->rows); 1272 if (proceed == MagickFalse) 1273 status=MagickFalse; 1274 } 1275 } 1276 image_view=DestroyCacheView(image_view); 1277 stretch_map=(double *) RelinquishMagickMemory(stretch_map); 1278 white=(double *) RelinquishMagickMemory(white); 1279 black=(double *) RelinquishMagickMemory(black); 1280 return(status); 1281 } 1282 1283 /* 1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1286 % % 1287 % % 1288 % % 1289 % E n h a n c e I m a g e % 1290 % % 1291 % % 1292 % % 1293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1294 % 1295 % EnhanceImage() applies a digital filter that improves the quality of a 1296 % noisy image. 1297 % 1298 % The format of the EnhanceImage method is: 1299 % 1300 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception) 1301 % 1302 % A description of each parameter follows: 1303 % 1304 % o image: the image. 1305 % 1306 % o exception: return any errors or warnings in this structure. 1307 % 1308 */ 1309 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception) 1310 { 1311 #define EnhanceImageTag "Enhance/Image" 1312 #define EnhancePixel(weight) \ 1313 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \ 1314 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \ 1315 distance_squared=(4.0+mean)*distance*distance; \ 1316 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \ 1317 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \ 1318 distance_squared+=(7.0-mean)*distance*distance; \ 1319 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \ 1320 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \ 1321 distance_squared+=(5.0-mean)*distance*distance; \ 1322 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \ 1323 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \ 1324 distance_squared+=(5.0-mean)*distance*distance; \ 1325 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \ 1326 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \ 1327 distance_squared+=(5.0-mean)*distance*distance; \ 1328 if (distance_squared < 0.069) \ 1329 { \ 1330 aggregate.red+=(weight)*GetPixelRed(image,r); \ 1331 aggregate.green+=(weight)*GetPixelGreen(image,r); \ 1332 aggregate.blue+=(weight)*GetPixelBlue(image,r); \ 1333 aggregate.black+=(weight)*GetPixelBlack(image,r); \ 1334 aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \ 1335 total_weight+=(weight); \ 1336 } \ 1337 r+=GetPixelChannels(image); 1338 1339 CacheView 1340 *enhance_view, 1341 *image_view; 1342 1343 Image 1344 *enhance_image; 1345 1346 MagickBooleanType 1347 status; 1348 1349 MagickOffsetType 1350 progress; 1351 1352 ssize_t 1353 y; 1354 1355 /* 1356 Initialize enhanced image attributes. 1357 */ 1358 assert(image != (const Image *) NULL); 1359 assert(image->signature == MagickCoreSignature); 1360 if (image->debug != MagickFalse) 1361 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1362 assert(exception != (ExceptionInfo *) NULL); 1363 assert(exception->signature == MagickCoreSignature); 1364 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue, 1365 exception); 1366 if (enhance_image == (Image *) NULL) 1367 return((Image *) NULL); 1368 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse) 1369 { 1370 enhance_image=DestroyImage(enhance_image); 1371 return((Image *) NULL); 1372 } 1373 /* 1374 Enhance image. 1375 */ 1376 status=MagickTrue; 1377 progress=0; 1378 image_view=AcquireVirtualCacheView(image,exception); 1379 enhance_view=AcquireAuthenticCacheView(enhance_image,exception); 1380 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1381 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1382 magick_threads(image,enhance_image,image->rows,1) 1383 #endif 1384 for (y=0; y < (ssize_t) image->rows; y++) 1385 { 1386 PixelInfo 1387 pixel; 1388 1389 register const Quantum 1390 *magick_restrict p; 1391 1392 register Quantum 1393 *magick_restrict q; 1394 1395 register ssize_t 1396 x; 1397 1398 ssize_t 1399 center; 1400 1401 if (status == MagickFalse) 1402 continue; 1403 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception); 1404 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1, 1405 exception); 1406 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1407 { 1408 status=MagickFalse; 1409 continue; 1410 } 1411 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2); 1412 GetPixelInfo(image,&pixel); 1413 for (x=0; x < (ssize_t) image->columns; x++) 1414 { 1415 double 1416 distance, 1417 distance_squared, 1418 mean, 1419 total_weight; 1420 1421 PixelInfo 1422 aggregate; 1423 1424 register const Quantum 1425 *magick_restrict r; 1426 1427 if (GetPixelReadMask(image,p) == 0) 1428 { 1429 SetPixelBackgoundColor(enhance_image,q); 1430 p+=GetPixelChannels(image); 1431 q+=GetPixelChannels(enhance_image); 1432 continue; 1433 } 1434 GetPixelInfo(image,&aggregate); 1435 total_weight=0.0; 1436 GetPixelInfoPixel(image,p+center,&pixel); 1437 r=p; 1438 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0); 1439 EnhancePixel(8.0); EnhancePixel(5.0); 1440 r=p+GetPixelChannels(image)*(image->columns+4); 1441 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0); 1442 EnhancePixel(20.0); EnhancePixel(8.0); 1443 r=p+2*GetPixelChannels(image)*(image->columns+4); 1444 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0); 1445 EnhancePixel(40.0); EnhancePixel(10.0); 1446 r=p+3*GetPixelChannels(image)*(image->columns+4); 1447 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0); 1448 EnhancePixel(20.0); EnhancePixel(8.0); 1449 r=p+4*GetPixelChannels(image)*(image->columns+4); 1450 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0); 1451 EnhancePixel(8.0); EnhancePixel(5.0); 1452 pixel.red=((aggregate.red+total_weight/2.0)/total_weight); 1453 pixel.green=((aggregate.green+total_weight/2.0)/total_weight); 1454 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight); 1455 pixel.black=((aggregate.black+total_weight/2.0)/total_weight); 1456 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight); 1457 SetPixelViaPixelInfo(image,&pixel,q); 1458 p+=GetPixelChannels(image); 1459 q+=GetPixelChannels(enhance_image); 1460 } 1461 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse) 1462 status=MagickFalse; 1463 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1464 { 1465 MagickBooleanType 1466 proceed; 1467 1468 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1469 #pragma omp critical (MagickCore_EnhanceImage) 1470 #endif 1471 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows); 1472 if (proceed == MagickFalse) 1473 status=MagickFalse; 1474 } 1475 } 1476 enhance_view=DestroyCacheView(enhance_view); 1477 image_view=DestroyCacheView(image_view); 1478 if (status == MagickFalse) 1479 enhance_image=DestroyImage(enhance_image); 1480 return(enhance_image); 1481 } 1482 1483 /* 1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1486 % % 1487 % % 1488 % % 1489 % E q u a l i z e I m a g e % 1490 % % 1491 % % 1492 % % 1493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1494 % 1495 % EqualizeImage() applies a histogram equalization to the image. 1496 % 1497 % The format of the EqualizeImage method is: 1498 % 1499 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception) 1500 % 1501 % A description of each parameter follows: 1502 % 1503 % o image: the image. 1504 % 1505 % o exception: return any errors or warnings in this structure. 1506 % 1507 */ 1508 MagickExport MagickBooleanType EqualizeImage(Image *image, 1509 ExceptionInfo *exception) 1510 { 1511 #define EqualizeImageTag "Equalize/Image" 1512 1513 CacheView 1514 *image_view; 1515 1516 double 1517 black[CompositePixelChannel+1], 1518 *equalize_map, 1519 *histogram, 1520 *map, 1521 white[CompositePixelChannel+1]; 1522 1523 MagickBooleanType 1524 status; 1525 1526 MagickOffsetType 1527 progress; 1528 1529 register ssize_t 1530 i; 1531 1532 ssize_t 1533 y; 1534 1535 /* 1536 Allocate and initialize histogram arrays. 1537 */ 1538 assert(image != (Image *) NULL); 1539 assert(image->signature == MagickCoreSignature); 1540 #if defined(MAGICKCORE_OPENCL_SUPPORT) 1541 if (AccelerateEqualizeImage(image,exception) != MagickFalse) 1542 return(MagickTrue); 1543 #endif 1544 if (image->debug != MagickFalse) 1545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1546 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL, 1547 GetPixelChannels(image)*sizeof(*equalize_map)); 1548 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)* 1549 sizeof(*histogram)); 1550 map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)* 1551 sizeof(*map)); 1552 if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) || 1553 (map == (double *) NULL)) 1554 { 1555 if (map != (double *) NULL) 1556 map=(double *) RelinquishMagickMemory(map); 1557 if (histogram != (double *) NULL) 1558 histogram=(double *) RelinquishMagickMemory(histogram); 1559 if (equalize_map != (double *) NULL) 1560 equalize_map=(double *) RelinquishMagickMemory(equalize_map); 1561 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 1562 image->filename); 1563 } 1564 /* 1565 Form histogram. 1566 */ 1567 status=MagickTrue; 1568 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)* 1569 sizeof(*histogram)); 1570 image_view=AcquireVirtualCacheView(image,exception); 1571 for (y=0; y < (ssize_t) image->rows; y++) 1572 { 1573 register const Quantum 1574 *magick_restrict p; 1575 1576 register ssize_t 1577 x; 1578 1579 if (status == MagickFalse) 1580 continue; 1581 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1582 if (p == (const Quantum *) NULL) 1583 { 1584 status=MagickFalse; 1585 continue; 1586 } 1587 for (x=0; x < (ssize_t) image->columns; x++) 1588 { 1589 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1590 { 1591 double 1592 intensity; 1593 1594 intensity=p[i]; 1595 if ((image->channel_mask & SyncChannels) != 0) 1596 intensity=GetPixelIntensity(image,p); 1597 histogram[GetPixelChannels(image)*ScaleQuantumToMap(intensity)+i]++; 1598 } 1599 p+=GetPixelChannels(image); 1600 } 1601 } 1602 image_view=DestroyCacheView(image_view); 1603 /* 1604 Integrate the histogram to get the equalization map. 1605 */ 1606 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1607 { 1608 double 1609 intensity; 1610 1611 register ssize_t 1612 j; 1613 1614 intensity=0.0; 1615 for (j=0; j <= (ssize_t) MaxMap; j++) 1616 { 1617 intensity+=histogram[GetPixelChannels(image)*j+i]; 1618 map[GetPixelChannels(image)*j+i]=intensity; 1619 } 1620 } 1621 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)* 1622 sizeof(*equalize_map)); 1623 (void) ResetMagickMemory(black,0,sizeof(*black)); 1624 (void) ResetMagickMemory(white,0,sizeof(*white)); 1625 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1626 { 1627 register ssize_t 1628 j; 1629 1630 black[i]=map[i]; 1631 white[i]=map[GetPixelChannels(image)*MaxMap+i]; 1632 if (black[i] != white[i]) 1633 for (j=0; j <= (ssize_t) MaxMap; j++) 1634 equalize_map[GetPixelChannels(image)*j+i]=(double) 1635 ScaleMapToQuantum((double) ((MaxMap*(map[ 1636 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i]))); 1637 } 1638 histogram=(double *) RelinquishMagickMemory(histogram); 1639 map=(double *) RelinquishMagickMemory(map); 1640 if (image->storage_class == PseudoClass) 1641 { 1642 register ssize_t 1643 j; 1644 1645 /* 1646 Equalize colormap. 1647 */ 1648 for (j=0; j < (ssize_t) image->colors; j++) 1649 { 1650 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 1651 { 1652 PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel); 1653 if (black[channel] != white[channel]) 1654 image->colormap[j].red=equalize_map[GetPixelChannels(image)* 1655 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+ 1656 channel]; 1657 } 1658 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 1659 { 1660 PixelChannel channel=GetPixelChannelChannel(image, 1661 GreenPixelChannel); 1662 if (black[channel] != white[channel]) 1663 image->colormap[j].green=equalize_map[GetPixelChannels(image)* 1664 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+ 1665 channel]; 1666 } 1667 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 1668 { 1669 PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel); 1670 if (black[channel] != white[channel]) 1671 image->colormap[j].blue=equalize_map[GetPixelChannels(image)* 1672 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+ 1673 channel]; 1674 } 1675 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 1676 { 1677 PixelChannel channel=GetPixelChannelChannel(image, 1678 AlphaPixelChannel); 1679 if (black[channel] != white[channel]) 1680 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)* 1681 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+ 1682 channel]; 1683 } 1684 } 1685 } 1686 /* 1687 Equalize image. 1688 */ 1689 progress=0; 1690 image_view=AcquireAuthenticCacheView(image,exception); 1691 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1692 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1693 magick_threads(image,image,image->rows,1) 1694 #endif 1695 for (y=0; y < (ssize_t) image->rows; y++) 1696 { 1697 register Quantum 1698 *magick_restrict q; 1699 1700 register ssize_t 1701 x; 1702 1703 if (status == MagickFalse) 1704 continue; 1705 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 1706 if (q == (Quantum *) NULL) 1707 { 1708 status=MagickFalse; 1709 continue; 1710 } 1711 for (x=0; x < (ssize_t) image->columns; x++) 1712 { 1713 register ssize_t 1714 j; 1715 1716 if (GetPixelReadMask(image,q) == 0) 1717 { 1718 q+=GetPixelChannels(image); 1719 continue; 1720 } 1721 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 1722 { 1723 PixelChannel channel=GetPixelChannelChannel(image,j); 1724 PixelTrait traits=GetPixelChannelTraits(image,channel); 1725 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j])) 1726 continue; 1727 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)* 1728 ScaleQuantumToMap(q[j])+j]); 1729 } 1730 q+=GetPixelChannels(image); 1731 } 1732 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1733 status=MagickFalse; 1734 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1735 { 1736 MagickBooleanType 1737 proceed; 1738 1739 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1740 #pragma omp critical (MagickCore_EqualizeImage) 1741 #endif 1742 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows); 1743 if (proceed == MagickFalse) 1744 status=MagickFalse; 1745 } 1746 } 1747 image_view=DestroyCacheView(image_view); 1748 equalize_map=(double *) RelinquishMagickMemory(equalize_map); 1749 return(status); 1750 } 1751 1752 /* 1754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1755 % % 1756 % % 1757 % % 1758 % G a m m a I m a g e % 1759 % % 1760 % % 1761 % % 1762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1763 % 1764 % GammaImage() gamma-corrects a particular image channel. The same 1765 % image viewed on different devices will have perceptual differences in the 1766 % way the image's intensities are represented on the screen. Specify 1767 % individual gamma levels for the red, green, and blue channels, or adjust 1768 % all three with the gamma parameter. Values typically range from 0.8 to 2.3. 1769 % 1770 % You can also reduce the influence of a particular channel with a gamma 1771 % value of 0. 1772 % 1773 % The format of the GammaImage method is: 1774 % 1775 % MagickBooleanType GammaImage(Image *image,const double gamma, 1776 % ExceptionInfo *exception) 1777 % 1778 % A description of each parameter follows: 1779 % 1780 % o image: the image. 1781 % 1782 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0). 1783 % 1784 % o gamma: the image gamma. 1785 % 1786 */ 1787 1788 static inline double gamma_pow(const double value,const double gamma) 1789 { 1790 return(value < 0.0 ? value : pow(value,gamma)); 1791 } 1792 1793 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma, 1794 ExceptionInfo *exception) 1795 { 1796 #define GammaCorrectImageTag "GammaCorrect/Image" 1797 1798 CacheView 1799 *image_view; 1800 1801 MagickBooleanType 1802 status; 1803 1804 MagickOffsetType 1805 progress; 1806 1807 Quantum 1808 *gamma_map; 1809 1810 register ssize_t 1811 i; 1812 1813 ssize_t 1814 y; 1815 1816 /* 1817 Allocate and initialize gamma maps. 1818 */ 1819 assert(image != (Image *) NULL); 1820 assert(image->signature == MagickCoreSignature); 1821 if (image->debug != MagickFalse) 1822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1823 if (gamma == 1.0) 1824 return(MagickTrue); 1825 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map)); 1826 if (gamma_map == (Quantum *) NULL) 1827 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 1828 image->filename); 1829 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map)); 1830 if (gamma != 0.0) 1831 for (i=0; i <= (ssize_t) MaxMap; i++) 1832 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/ 1833 MaxMap,1.0/gamma))); 1834 if (image->storage_class == PseudoClass) 1835 for (i=0; i < (ssize_t) image->colors; i++) 1836 { 1837 /* 1838 Gamma-correct colormap. 1839 */ 1840 #if !defined(MAGICKCORE_HDRI_SUPPORT) 1841 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 1842 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap( 1843 ClampToQuantum(image->colormap[i].red))]; 1844 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 1845 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap( 1846 ClampToQuantum(image->colormap[i].green))]; 1847 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 1848 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap( 1849 ClampToQuantum(image->colormap[i].blue))]; 1850 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 1851 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap( 1852 ClampToQuantum(image->colormap[i].alpha))]; 1853 #else 1854 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 1855 image->colormap[i].red=QuantumRange*gamma_pow(QuantumScale* 1856 image->colormap[i].red,1.0/gamma); 1857 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 1858 image->colormap[i].green=QuantumRange*gamma_pow(QuantumScale* 1859 image->colormap[i].green,1.0/gamma); 1860 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 1861 image->colormap[i].blue=QuantumRange*gamma_pow(QuantumScale* 1862 image->colormap[i].blue,1.0/gamma); 1863 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 1864 image->colormap[i].alpha=QuantumRange*gamma_pow(QuantumScale* 1865 image->colormap[i].alpha,1.0/gamma); 1866 #endif 1867 } 1868 /* 1869 Gamma-correct image. 1870 */ 1871 status=MagickTrue; 1872 progress=0; 1873 image_view=AcquireAuthenticCacheView(image,exception); 1874 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1875 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1876 magick_threads(image,image,image->rows,1) 1877 #endif 1878 for (y=0; y < (ssize_t) image->rows; y++) 1879 { 1880 register Quantum 1881 *magick_restrict q; 1882 1883 register ssize_t 1884 x; 1885 1886 if (status == MagickFalse) 1887 continue; 1888 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 1889 if (q == (Quantum *) NULL) 1890 { 1891 status=MagickFalse; 1892 continue; 1893 } 1894 for (x=0; x < (ssize_t) image->columns; x++) 1895 { 1896 register ssize_t 1897 j; 1898 1899 if (GetPixelReadMask(image,q) == 0) 1900 { 1901 q+=GetPixelChannels(image); 1902 continue; 1903 } 1904 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 1905 { 1906 PixelChannel channel=GetPixelChannelChannel(image,j); 1907 PixelTrait traits=GetPixelChannelTraits(image,channel); 1908 if ((traits & UpdatePixelTrait) == 0) 1909 continue; 1910 #if !defined(MAGICKCORE_HDRI_SUPPORT) 1911 q[j]=gamma_map[ScaleQuantumToMap(q[j])]; 1912 #else 1913 q[j]=QuantumRange*gamma_pow(QuantumScale*q[j],1.0/gamma); 1914 #endif 1915 } 1916 q+=GetPixelChannels(image); 1917 } 1918 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1919 status=MagickFalse; 1920 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1921 { 1922 MagickBooleanType 1923 proceed; 1924 1925 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1926 #pragma omp critical (MagickCore_GammaImage) 1927 #endif 1928 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++, 1929 image->rows); 1930 if (proceed == MagickFalse) 1931 status=MagickFalse; 1932 } 1933 } 1934 image_view=DestroyCacheView(image_view); 1935 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map); 1936 if (image->gamma != 0.0) 1937 image->gamma*=gamma; 1938 return(status); 1939 } 1940 1941 /* 1943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1944 % % 1945 % % 1946 % % 1947 % G r a y s c a l e I m a g e % 1948 % % 1949 % % 1950 % % 1951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1952 % 1953 % GrayscaleImage() converts the image to grayscale. 1954 % 1955 % The format of the GrayscaleImage method is: 1956 % 1957 % MagickBooleanType GrayscaleImage(Image *image, 1958 % const PixelIntensityMethod method ,ExceptionInfo *exception) 1959 % 1960 % A description of each parameter follows: 1961 % 1962 % o image: the image. 1963 % 1964 % o method: the pixel intensity method. 1965 % 1966 % o exception: return any errors or warnings in this structure. 1967 % 1968 */ 1969 MagickExport MagickBooleanType GrayscaleImage(Image *image, 1970 const PixelIntensityMethod method,ExceptionInfo *exception) 1971 { 1972 #define GrayscaleImageTag "Grayscale/Image" 1973 1974 CacheView 1975 *image_view; 1976 1977 MagickBooleanType 1978 status; 1979 1980 MagickOffsetType 1981 progress; 1982 1983 ssize_t 1984 y; 1985 1986 assert(image != (Image *) NULL); 1987 assert(image->signature == MagickCoreSignature); 1988 if (image->debug != MagickFalse) 1989 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1990 if (image->storage_class == PseudoClass) 1991 { 1992 if (SyncImage(image,exception) == MagickFalse) 1993 return(MagickFalse); 1994 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 1995 return(MagickFalse); 1996 } 1997 #if defined(MAGICKCORE_OPENCL_SUPPORT) 1998 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse) 1999 { 2000 image->intensity=method; 2001 image->type=GrayscaleType; 2002 return(SetImageColorspace(image,GRAYColorspace,exception)); 2003 } 2004 #endif 2005 /* 2006 Grayscale image. 2007 */ 2008 status=MagickTrue; 2009 progress=0; 2010 image_view=AcquireAuthenticCacheView(image,exception); 2011 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2012 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2013 magick_threads(image,image,image->rows,1) 2014 #endif 2015 for (y=0; y < (ssize_t) image->rows; y++) 2016 { 2017 register Quantum 2018 *magick_restrict q; 2019 2020 register ssize_t 2021 x; 2022 2023 if (status == MagickFalse) 2024 continue; 2025 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 2026 if (q == (Quantum *) NULL) 2027 { 2028 status=MagickFalse; 2029 continue; 2030 } 2031 for (x=0; x < (ssize_t) image->columns; x++) 2032 { 2033 MagickRealType 2034 blue, 2035 green, 2036 red, 2037 intensity; 2038 2039 if (GetPixelReadMask(image,q) == 0) 2040 { 2041 q+=GetPixelChannels(image); 2042 continue; 2043 } 2044 red=(MagickRealType) GetPixelRed(image,q); 2045 green=(MagickRealType) GetPixelGreen(image,q); 2046 blue=(MagickRealType) GetPixelBlue(image,q); 2047 intensity=0.0; 2048 switch (method) 2049 { 2050 case AveragePixelIntensityMethod: 2051 { 2052 intensity=(red+green+blue)/3.0; 2053 break; 2054 } 2055 case BrightnessPixelIntensityMethod: 2056 { 2057 intensity=MagickMax(MagickMax(red,green),blue); 2058 break; 2059 } 2060 case LightnessPixelIntensityMethod: 2061 { 2062 intensity=(MagickMin(MagickMin(red,green),blue)+ 2063 MagickMax(MagickMax(red,green),blue))/2.0; 2064 break; 2065 } 2066 case MSPixelIntensityMethod: 2067 { 2068 intensity=(MagickRealType) (((double) red*red+green*green+ 2069 blue*blue)/3.0); 2070 break; 2071 } 2072 case Rec601LumaPixelIntensityMethod: 2073 { 2074 if (image->colorspace == RGBColorspace) 2075 { 2076 red=EncodePixelGamma(red); 2077 green=EncodePixelGamma(green); 2078 blue=EncodePixelGamma(blue); 2079 } 2080 intensity=0.298839*red+0.586811*green+0.114350*blue; 2081 break; 2082 } 2083 case Rec601LuminancePixelIntensityMethod: 2084 { 2085 if (image->colorspace == sRGBColorspace) 2086 { 2087 red=DecodePixelGamma(red); 2088 green=DecodePixelGamma(green); 2089 blue=DecodePixelGamma(blue); 2090 } 2091 intensity=0.298839*red+0.586811*green+0.114350*blue; 2092 break; 2093 } 2094 case Rec709LumaPixelIntensityMethod: 2095 default: 2096 { 2097 if (image->colorspace == RGBColorspace) 2098 { 2099 red=EncodePixelGamma(red); 2100 green=EncodePixelGamma(green); 2101 blue=EncodePixelGamma(blue); 2102 } 2103 intensity=0.212656*red+0.715158*green+0.072186*blue; 2104 break; 2105 } 2106 case Rec709LuminancePixelIntensityMethod: 2107 { 2108 if (image->colorspace == sRGBColorspace) 2109 { 2110 red=DecodePixelGamma(red); 2111 green=DecodePixelGamma(green); 2112 blue=DecodePixelGamma(blue); 2113 } 2114 intensity=0.212656*red+0.715158*green+0.072186*blue; 2115 break; 2116 } 2117 case RMSPixelIntensityMethod: 2118 { 2119 intensity=(MagickRealType) (sqrt((double) red*red+green*green+ 2120 blue*blue)/sqrt(3.0)); 2121 break; 2122 } 2123 } 2124 SetPixelGray(image,ClampToQuantum(intensity),q); 2125 q+=GetPixelChannels(image); 2126 } 2127 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 2128 status=MagickFalse; 2129 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2130 { 2131 MagickBooleanType 2132 proceed; 2133 2134 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2135 #pragma omp critical (MagickCore_GrayscaleImage) 2136 #endif 2137 proceed=SetImageProgress(image,GrayscaleImageTag,progress++, 2138 image->rows); 2139 if (proceed == MagickFalse) 2140 status=MagickFalse; 2141 } 2142 } 2143 image_view=DestroyCacheView(image_view); 2144 image->intensity=method; 2145 image->type=GrayscaleType; 2146 return(SetImageColorspace(image,GRAYColorspace,exception)); 2147 } 2148 2149 /* 2151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2152 % % 2153 % % 2154 % % 2155 % H a l d C l u t I m a g e % 2156 % % 2157 % % 2158 % % 2159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2160 % 2161 % HaldClutImage() applies a Hald color lookup table to the image. A Hald 2162 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions. 2163 % Create it with the HALD coder. You can apply any color transformation to 2164 % the Hald image and then use this method to apply the transform to the 2165 % image. 2166 % 2167 % The format of the HaldClutImage method is: 2168 % 2169 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image, 2170 % ExceptionInfo *exception) 2171 % 2172 % A description of each parameter follows: 2173 % 2174 % o image: the image, which is replaced by indexed CLUT values 2175 % 2176 % o hald_image: the color lookup table image for replacement color values. 2177 % 2178 % o exception: return any errors or warnings in this structure. 2179 % 2180 */ 2181 MagickExport MagickBooleanType HaldClutImage(Image *image, 2182 const Image *hald_image,ExceptionInfo *exception) 2183 { 2184 #define HaldClutImageTag "Clut/Image" 2185 2186 typedef struct _HaldInfo 2187 { 2188 double 2189 x, 2190 y, 2191 z; 2192 } HaldInfo; 2193 2194 CacheView 2195 *hald_view, 2196 *image_view; 2197 2198 double 2199 width; 2200 2201 MagickBooleanType 2202 status; 2203 2204 MagickOffsetType 2205 progress; 2206 2207 PixelInfo 2208 zero; 2209 2210 size_t 2211 cube_size, 2212 length, 2213 level; 2214 2215 ssize_t 2216 y; 2217 2218 assert(image != (Image *) NULL); 2219 assert(image->signature == MagickCoreSignature); 2220 if (image->debug != MagickFalse) 2221 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2222 assert(hald_image != (Image *) NULL); 2223 assert(hald_image->signature == MagickCoreSignature); 2224 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 2225 return(MagickFalse); 2226 if (image->alpha_trait == UndefinedPixelTrait) 2227 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 2228 /* 2229 Hald clut image. 2230 */ 2231 status=MagickTrue; 2232 progress=0; 2233 length=(size_t) MagickMin((MagickRealType) hald_image->columns, 2234 (MagickRealType) hald_image->rows); 2235 for (level=2; (level*level*level) < length; level++) ; 2236 level*=level; 2237 cube_size=level*level; 2238 width=(double) hald_image->columns; 2239 GetPixelInfo(hald_image,&zero); 2240 hald_view=AcquireVirtualCacheView(hald_image,exception); 2241 image_view=AcquireAuthenticCacheView(image,exception); 2242 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2243 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2244 magick_threads(image,image,image->rows,1) 2245 #endif 2246 for (y=0; y < (ssize_t) image->rows; y++) 2247 { 2248 register Quantum 2249 *magick_restrict q; 2250 2251 register ssize_t 2252 x; 2253 2254 if (status == MagickFalse) 2255 continue; 2256 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 2257 if (q == (Quantum *) NULL) 2258 { 2259 status=MagickFalse; 2260 continue; 2261 } 2262 for (x=0; x < (ssize_t) image->columns; x++) 2263 { 2264 double 2265 offset; 2266 2267 HaldInfo 2268 point; 2269 2270 PixelInfo 2271 pixel, 2272 pixel1, 2273 pixel2, 2274 pixel3, 2275 pixel4; 2276 2277 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q); 2278 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q); 2279 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q); 2280 offset=point.x+level*floor(point.y)+cube_size*floor(point.z); 2281 point.x-=floor(point.x); 2282 point.y-=floor(point.y); 2283 point.z-=floor(point.z); 2284 pixel1=zero; 2285 (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate, 2286 fmod(offset,width),floor(offset/width),&pixel1,exception); 2287 pixel2=zero; 2288 (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate, 2289 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception); 2290 pixel3=zero; 2291 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha, 2292 point.y,&pixel3); 2293 offset+=cube_size; 2294 (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate, 2295 fmod(offset,width),floor(offset/width),&pixel1,exception); 2296 (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate, 2297 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception); 2298 pixel4=zero; 2299 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha, 2300 point.y,&pixel4); 2301 pixel=zero; 2302 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha, 2303 point.z,&pixel); 2304 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 2305 SetPixelRed(image,ClampToQuantum(pixel.red),q); 2306 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 2307 SetPixelGreen(image,ClampToQuantum(pixel.green),q); 2308 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 2309 SetPixelBlue(image,ClampToQuantum(pixel.blue),q); 2310 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 2311 (image->colorspace == CMYKColorspace)) 2312 SetPixelBlack(image,ClampToQuantum(pixel.black),q); 2313 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 2314 (image->alpha_trait != UndefinedPixelTrait)) 2315 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); 2316 q+=GetPixelChannels(image); 2317 } 2318 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 2319 status=MagickFalse; 2320 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2321 { 2322 MagickBooleanType 2323 proceed; 2324 2325 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2326 #pragma omp critical (MagickCore_HaldClutImage) 2327 #endif 2328 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows); 2329 if (proceed == MagickFalse) 2330 status=MagickFalse; 2331 } 2332 } 2333 hald_view=DestroyCacheView(hald_view); 2334 image_view=DestroyCacheView(image_view); 2335 return(status); 2336 } 2337 2338 /* 2340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2341 % % 2342 % % 2343 % % 2344 % L e v e l I m a g e % 2345 % % 2346 % % 2347 % % 2348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2349 % 2350 % LevelImage() adjusts the levels of a particular image channel by 2351 % scaling the colors falling between specified white and black points to 2352 % the full available quantum range. 2353 % 2354 % The parameters provided represent the black, and white points. The black 2355 % point specifies the darkest color in the image. Colors darker than the 2356 % black point are set to zero. White point specifies the lightest color in 2357 % the image. Colors brighter than the white point are set to the maximum 2358 % quantum value. 2359 % 2360 % If a '!' flag is given, map black and white colors to the given levels 2361 % rather than mapping those levels to black and white. See 2362 % LevelizeImage() below. 2363 % 2364 % Gamma specifies a gamma correction to apply to the image. 2365 % 2366 % The format of the LevelImage method is: 2367 % 2368 % MagickBooleanType LevelImage(Image *image,const double black_point, 2369 % const double white_point,const double gamma,ExceptionInfo *exception) 2370 % 2371 % A description of each parameter follows: 2372 % 2373 % o image: the image. 2374 % 2375 % o black_point: The level to map zero (black) to. 2376 % 2377 % o white_point: The level to map QuantumRange (white) to. 2378 % 2379 % o exception: return any errors or warnings in this structure. 2380 % 2381 */ 2382 2383 static inline double LevelPixel(const double black_point, 2384 const double white_point,const double gamma,const double pixel) 2385 { 2386 double 2387 level_pixel, 2388 scale; 2389 2390 if (fabs(white_point-black_point) < MagickEpsilon) 2391 return(pixel); 2392 scale=1.0/(white_point-black_point); 2393 level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point), 2394 1.0/gamma); 2395 return(level_pixel); 2396 } 2397 2398 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point, 2399 const double white_point,const double gamma,ExceptionInfo *exception) 2400 { 2401 #define LevelImageTag "Level/Image" 2402 2403 CacheView 2404 *image_view; 2405 2406 MagickBooleanType 2407 status; 2408 2409 MagickOffsetType 2410 progress; 2411 2412 register ssize_t 2413 i; 2414 2415 ssize_t 2416 y; 2417 2418 /* 2419 Allocate and initialize levels map. 2420 */ 2421 assert(image != (Image *) NULL); 2422 assert(image->signature == MagickCoreSignature); 2423 if (image->debug != MagickFalse) 2424 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2425 if (image->storage_class == PseudoClass) 2426 for (i=0; i < (ssize_t) image->colors; i++) 2427 { 2428 /* 2429 Level colormap. 2430 */ 2431 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 2432 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point, 2433 white_point,gamma,image->colormap[i].red)); 2434 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 2435 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point, 2436 white_point,gamma,image->colormap[i].green)); 2437 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 2438 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point, 2439 white_point,gamma,image->colormap[i].blue)); 2440 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 2441 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point, 2442 white_point,gamma,image->colormap[i].alpha)); 2443 } 2444 /* 2445 Level image. 2446 */ 2447 status=MagickTrue; 2448 progress=0; 2449 image_view=AcquireAuthenticCacheView(image,exception); 2450 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2451 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2452 magick_threads(image,image,image->rows,1) 2453 #endif 2454 for (y=0; y < (ssize_t) image->rows; y++) 2455 { 2456 register Quantum 2457 *magick_restrict q; 2458 2459 register ssize_t 2460 x; 2461 2462 if (status == MagickFalse) 2463 continue; 2464 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 2465 if (q == (Quantum *) NULL) 2466 { 2467 status=MagickFalse; 2468 continue; 2469 } 2470 for (x=0; x < (ssize_t) image->columns; x++) 2471 { 2472 register ssize_t 2473 j; 2474 2475 if (GetPixelReadMask(image,q) == 0) 2476 { 2477 q+=GetPixelChannels(image); 2478 continue; 2479 } 2480 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 2481 { 2482 PixelChannel channel=GetPixelChannelChannel(image,j); 2483 PixelTrait traits=GetPixelChannelTraits(image,channel); 2484 if ((traits & UpdatePixelTrait) == 0) 2485 continue; 2486 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma, 2487 (double) q[j])); 2488 } 2489 q+=GetPixelChannels(image); 2490 } 2491 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 2492 status=MagickFalse; 2493 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2494 { 2495 MagickBooleanType 2496 proceed; 2497 2498 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2499 #pragma omp critical (MagickCore_LevelImage) 2500 #endif 2501 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows); 2502 if (proceed == MagickFalse) 2503 status=MagickFalse; 2504 } 2505 } 2506 image_view=DestroyCacheView(image_view); 2507 (void) ClampImage(image,exception); 2508 return(status); 2509 } 2510 2511 /* 2513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2514 % % 2515 % % 2516 % % 2517 % L e v e l i z e I m a g e % 2518 % % 2519 % % 2520 % % 2521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2522 % 2523 % LevelizeImage() applies the reversed LevelImage() operation to just 2524 % the specific channels specified. It compresses the full range of color 2525 % values, so that they lie between the given black and white points. Gamma is 2526 % applied before the values are mapped. 2527 % 2528 % LevelizeImage() can be called with by using a +level command line 2529 % API option, or using a '!' on a -level or LevelImage() geometry string. 2530 % 2531 % It can be used to de-contrast a greyscale image to the exact levels 2532 % specified. Or by using specific levels for each channel of an image you 2533 % can convert a gray-scale image to any linear color gradient, according to 2534 % those levels. 2535 % 2536 % The format of the LevelizeImage method is: 2537 % 2538 % MagickBooleanType LevelizeImage(Image *image,const double black_point, 2539 % const double white_point,const double gamma,ExceptionInfo *exception) 2540 % 2541 % A description of each parameter follows: 2542 % 2543 % o image: the image. 2544 % 2545 % o black_point: The level to map zero (black) to. 2546 % 2547 % o white_point: The level to map QuantumRange (white) to. 2548 % 2549 % o gamma: adjust gamma by this factor before mapping values. 2550 % 2551 % o exception: return any errors or warnings in this structure. 2552 % 2553 */ 2554 MagickExport MagickBooleanType LevelizeImage(Image *image, 2555 const double black_point,const double white_point,const double gamma, 2556 ExceptionInfo *exception) 2557 { 2558 #define LevelizeImageTag "Levelize/Image" 2559 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \ 2560 (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point) 2561 2562 CacheView 2563 *image_view; 2564 2565 MagickBooleanType 2566 status; 2567 2568 MagickOffsetType 2569 progress; 2570 2571 register ssize_t 2572 i; 2573 2574 ssize_t 2575 y; 2576 2577 /* 2578 Allocate and initialize levels map. 2579 */ 2580 assert(image != (Image *) NULL); 2581 assert(image->signature == MagickCoreSignature); 2582 if (image->debug != MagickFalse) 2583 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2584 if (image->storage_class == PseudoClass) 2585 for (i=0; i < (ssize_t) image->colors; i++) 2586 { 2587 /* 2588 Level colormap. 2589 */ 2590 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 2591 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red); 2592 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 2593 image->colormap[i].green=(double) LevelizeValue( 2594 image->colormap[i].green); 2595 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 2596 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue); 2597 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 2598 image->colormap[i].alpha=(double) LevelizeValue( 2599 image->colormap[i].alpha); 2600 } 2601 /* 2602 Level image. 2603 */ 2604 status=MagickTrue; 2605 progress=0; 2606 image_view=AcquireAuthenticCacheView(image,exception); 2607 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2608 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2609 magick_threads(image,image,image->rows,1) 2610 #endif 2611 for (y=0; y < (ssize_t) image->rows; y++) 2612 { 2613 register Quantum 2614 *magick_restrict q; 2615 2616 register ssize_t 2617 x; 2618 2619 if (status == MagickFalse) 2620 continue; 2621 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 2622 if (q == (Quantum *) NULL) 2623 { 2624 status=MagickFalse; 2625 continue; 2626 } 2627 for (x=0; x < (ssize_t) image->columns; x++) 2628 { 2629 register ssize_t 2630 j; 2631 2632 if (GetPixelReadMask(image,q) == 0) 2633 { 2634 q+=GetPixelChannels(image); 2635 continue; 2636 } 2637 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 2638 { 2639 PixelChannel channel=GetPixelChannelChannel(image,j); 2640 PixelTrait traits=GetPixelChannelTraits(image,channel); 2641 if ((traits & UpdatePixelTrait) == 0) 2642 continue; 2643 q[j]=LevelizeValue(q[j]); 2644 } 2645 q+=GetPixelChannels(image); 2646 } 2647 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 2648 status=MagickFalse; 2649 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2650 { 2651 MagickBooleanType 2652 proceed; 2653 2654 #if defined(MAGICKCORE_OPENMP_SUPPORT) 2655 #pragma omp critical (MagickCore_LevelizeImage) 2656 #endif 2657 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows); 2658 if (proceed == MagickFalse) 2659 status=MagickFalse; 2660 } 2661 } 2662 image_view=DestroyCacheView(image_view); 2663 return(status); 2664 } 2665 2666 /* 2668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2669 % % 2670 % % 2671 % % 2672 % L e v e l I m a g e C o l o r s % 2673 % % 2674 % % 2675 % % 2676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2677 % 2678 % LevelImageColors() maps the given color to "black" and "white" values, 2679 % linearly spreading out the colors, and level values on a channel by channel 2680 % bases, as per LevelImage(). The given colors allows you to specify 2681 % different level ranges for each of the color channels separately. 2682 % 2683 % If the boolean 'invert' is set true the image values will modifyed in the 2684 % reverse direction. That is any existing "black" and "white" colors in the 2685 % image will become the color values given, with all other values compressed 2686 % appropriatally. This effectivally maps a greyscale gradient into the given 2687 % color gradient. 2688 % 2689 % The format of the LevelImageColors method is: 2690 % 2691 % MagickBooleanType LevelImageColors(Image *image, 2692 % const PixelInfo *black_color,const PixelInfo *white_color, 2693 % const MagickBooleanType invert,ExceptionInfo *exception) 2694 % 2695 % A description of each parameter follows: 2696 % 2697 % o image: the image. 2698 % 2699 % o black_color: The color to map black to/from 2700 % 2701 % o white_point: The color to map white to/from 2702 % 2703 % o invert: if true map the colors (levelize), rather than from (level) 2704 % 2705 % o exception: return any errors or warnings in this structure. 2706 % 2707 */ 2708 MagickExport MagickBooleanType LevelImageColors(Image *image, 2709 const PixelInfo *black_color,const PixelInfo *white_color, 2710 const MagickBooleanType invert,ExceptionInfo *exception) 2711 { 2712 ChannelType 2713 channel_mask; 2714 2715 MagickStatusType 2716 status; 2717 2718 /* 2719 Allocate and initialize levels map. 2720 */ 2721 assert(image != (Image *) NULL); 2722 assert(image->signature == MagickCoreSignature); 2723 if (image->debug != MagickFalse) 2724 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2725 if ((IsGrayColorspace(image->colorspace) != MagickFalse) && 2726 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) || 2727 (IsGrayColorspace(white_color->colorspace) == MagickFalse))) 2728 (void) SetImageColorspace(image,sRGBColorspace,exception); 2729 status=MagickTrue; 2730 if (invert == MagickFalse) 2731 { 2732 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 2733 { 2734 channel_mask=SetImageChannelMask(image,RedChannel); 2735 status&=LevelImage(image,black_color->red,white_color->red,1.0, 2736 exception); 2737 (void) SetImageChannelMask(image,channel_mask); 2738 } 2739 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 2740 { 2741 channel_mask=SetImageChannelMask(image,GreenChannel); 2742 status&=LevelImage(image,black_color->green,white_color->green,1.0, 2743 exception); 2744 (void) SetImageChannelMask(image,channel_mask); 2745 } 2746 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 2747 { 2748 channel_mask=SetImageChannelMask(image,BlueChannel); 2749 status&=LevelImage(image,black_color->blue,white_color->blue,1.0, 2750 exception); 2751 (void) SetImageChannelMask(image,channel_mask); 2752 } 2753 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 2754 (image->colorspace == CMYKColorspace)) 2755 { 2756 channel_mask=SetImageChannelMask(image,BlackChannel); 2757 status&=LevelImage(image,black_color->black,white_color->black,1.0, 2758 exception); 2759 (void) SetImageChannelMask(image,channel_mask); 2760 } 2761 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 2762 (image->alpha_trait != UndefinedPixelTrait)) 2763 { 2764 channel_mask=SetImageChannelMask(image,AlphaChannel); 2765 status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0, 2766 exception); 2767 (void) SetImageChannelMask(image,channel_mask); 2768 } 2769 } 2770 else 2771 { 2772 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 2773 { 2774 channel_mask=SetImageChannelMask(image,RedChannel); 2775 status&=LevelizeImage(image,black_color->red,white_color->red,1.0, 2776 exception); 2777 (void) SetImageChannelMask(image,channel_mask); 2778 } 2779 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 2780 { 2781 channel_mask=SetImageChannelMask(image,GreenChannel); 2782 status&=LevelizeImage(image,black_color->green,white_color->green,1.0, 2783 exception); 2784 (void) SetImageChannelMask(image,channel_mask); 2785 } 2786 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 2787 { 2788 channel_mask=SetImageChannelMask(image,BlueChannel); 2789 status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0, 2790 exception); 2791 (void) SetImageChannelMask(image,channel_mask); 2792 } 2793 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 2794 (image->colorspace == CMYKColorspace)) 2795 { 2796 channel_mask=SetImageChannelMask(image,BlackChannel); 2797 status&=LevelizeImage(image,black_color->black,white_color->black,1.0, 2798 exception); 2799 (void) SetImageChannelMask(image,channel_mask); 2800 } 2801 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 2802 (image->alpha_trait != UndefinedPixelTrait)) 2803 { 2804 channel_mask=SetImageChannelMask(image,AlphaChannel); 2805 status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0, 2806 exception); 2807 (void) SetImageChannelMask(image,channel_mask); 2808 } 2809 } 2810 return(status != 0 ? MagickTrue : MagickFalse); 2811 } 2812 2813 /* 2815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2816 % % 2817 % % 2818 % % 2819 % L i n e a r S t r e t c h I m a g e % 2820 % % 2821 % % 2822 % % 2823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2824 % 2825 % LinearStretchImage() discards any pixels below the black point and above 2826 % the white point and levels the remaining pixels. 2827 % 2828 % The format of the LinearStretchImage method is: 2829 % 2830 % MagickBooleanType LinearStretchImage(Image *image, 2831 % const double black_point,const double white_point, 2832 % ExceptionInfo *exception) 2833 % 2834 % A description of each parameter follows: 2835 % 2836 % o image: the image. 2837 % 2838 % o black_point: the black point. 2839 % 2840 % o white_point: the white point. 2841 % 2842 % o exception: return any errors or warnings in this structure. 2843 % 2844 */ 2845 MagickExport MagickBooleanType LinearStretchImage(Image *image, 2846 const double black_point,const double white_point,ExceptionInfo *exception) 2847 { 2848 #define LinearStretchImageTag "LinearStretch/Image" 2849 2850 CacheView 2851 *image_view; 2852 2853 double 2854 *histogram, 2855 intensity; 2856 2857 MagickBooleanType 2858 status; 2859 2860 ssize_t 2861 black, 2862 white, 2863 y; 2864 2865 /* 2866 Allocate histogram and linear map. 2867 */ 2868 assert(image != (Image *) NULL); 2869 assert(image->signature == MagickCoreSignature); 2870 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram)); 2871 if (histogram == (double *) NULL) 2872 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 2873 image->filename); 2874 /* 2875 Form histogram. 2876 */ 2877 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram)); 2878 image_view=AcquireVirtualCacheView(image,exception); 2879 for (y=0; y < (ssize_t) image->rows; y++) 2880 { 2881 register const Quantum 2882 *magick_restrict p; 2883 2884 register ssize_t 2885 x; 2886 2887 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 2888 if (p == (const Quantum *) NULL) 2889 break; 2890 for (x=0; x < (ssize_t) image->columns; x++) 2891 { 2892 intensity=GetPixelIntensity(image,p); 2893 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++; 2894 p+=GetPixelChannels(image); 2895 } 2896 } 2897 image_view=DestroyCacheView(image_view); 2898 /* 2899 Find the histogram boundaries by locating the black and white point levels. 2900 */ 2901 intensity=0.0; 2902 for (black=0; black < (ssize_t) MaxMap; black++) 2903 { 2904 intensity+=histogram[black]; 2905 if (intensity >= black_point) 2906 break; 2907 } 2908 intensity=0.0; 2909 for (white=(ssize_t) MaxMap; white != 0; white--) 2910 { 2911 intensity+=histogram[white]; 2912 if (intensity >= white_point) 2913 break; 2914 } 2915 histogram=(double *) RelinquishMagickMemory(histogram); 2916 status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black), 2917 (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception); 2918 return(status); 2919 } 2920 2921 2922 /* 2923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2924 % % 2925 % % 2926 % % 2927 % M o d u l a t e I m a g e % 2928 % % 2929 % % 2930 % % 2931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2932 % 2933 % ModulateImage() lets you control the brightness, saturation, and hue 2934 % of an image. Modulate represents the brightness, saturation, and hue 2935 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the 2936 % modulation is lightness, saturation, and hue. For HWB, use blackness, 2937 % whiteness, and hue. And for HCL, use chrome, luma, and hue. 2938 % 2939 % The format of the ModulateImage method is: 2940 % 2941 % MagickBooleanType ModulateImage(Image *image,const char *modulate, 2942 % ExceptionInfo *exception) 2943 % 2944 % A description of each parameter follows: 2945 % 2946 % o image: the image. 2947 % 2948 % o modulate: Define the percent change in brightness, saturation, and hue. 2949 % 2950 % o exception: return any errors or warnings in this structure. 2951 % 2952 */ 2953 2954 static inline void ModulateHCL(const double percent_hue, 2955 const double percent_chroma,const double percent_luma,double *red, 2956 double *green,double *blue) 2957 { 2958 double 2959 hue, 2960 luma, 2961 chroma; 2962 2963 /* 2964 Increase or decrease color luma, chroma, or hue. 2965 */ 2966 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma); 2967 hue+=0.5*(0.01*percent_hue-1.0); 2968 while (hue < 0.0) 2969 hue+=1.0; 2970 while (hue > 1.0) 2971 hue-=1.0; 2972 chroma*=0.01*percent_chroma; 2973 luma*=0.01*percent_luma; 2974 ConvertHCLToRGB(hue,chroma,luma,red,green,blue); 2975 } 2976 2977 static inline void ModulateHCLp(const double percent_hue, 2978 const double percent_chroma,const double percent_luma,double *red, 2979 double *green,double *blue) 2980 { 2981 double 2982 hue, 2983 luma, 2984 chroma; 2985 2986 /* 2987 Increase or decrease color luma, chroma, or hue. 2988 */ 2989 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma); 2990 hue+=0.5*(0.01*percent_hue-1.0); 2991 while (hue < 0.0) 2992 hue+=1.0; 2993 while (hue > 1.0) 2994 hue-=1.0; 2995 chroma*=0.01*percent_chroma; 2996 luma*=0.01*percent_luma; 2997 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue); 2998 } 2999 3000 static inline void ModulateHSB(const double percent_hue, 3001 const double percent_saturation,const double percent_brightness,double *red, 3002 double *green,double *blue) 3003 { 3004 double 3005 brightness, 3006 hue, 3007 saturation; 3008 3009 /* 3010 Increase or decrease color brightness, saturation, or hue. 3011 */ 3012 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness); 3013 hue+=0.5*(0.01*percent_hue-1.0); 3014 while (hue < 0.0) 3015 hue+=1.0; 3016 while (hue > 1.0) 3017 hue-=1.0; 3018 saturation*=0.01*percent_saturation; 3019 brightness*=0.01*percent_brightness; 3020 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue); 3021 } 3022 3023 static inline void ModulateHSI(const double percent_hue, 3024 const double percent_saturation,const double percent_intensity,double *red, 3025 double *green,double *blue) 3026 { 3027 double 3028 intensity, 3029 hue, 3030 saturation; 3031 3032 /* 3033 Increase or decrease color intensity, saturation, or hue. 3034 */ 3035 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity); 3036 hue+=0.5*(0.01*percent_hue-1.0); 3037 while (hue < 0.0) 3038 hue+=1.0; 3039 while (hue > 1.0) 3040 hue-=1.0; 3041 saturation*=0.01*percent_saturation; 3042 intensity*=0.01*percent_intensity; 3043 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue); 3044 } 3045 3046 static inline void ModulateHSL(const double percent_hue, 3047 const double percent_saturation,const double percent_lightness,double *red, 3048 double *green,double *blue) 3049 { 3050 double 3051 hue, 3052 lightness, 3053 saturation; 3054 3055 /* 3056 Increase or decrease color lightness, saturation, or hue. 3057 */ 3058 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness); 3059 hue+=0.5*(0.01*percent_hue-1.0); 3060 while (hue < 0.0) 3061 hue+=1.0; 3062 while (hue >= 1.0) 3063 hue-=1.0; 3064 saturation*=0.01*percent_saturation; 3065 lightness*=0.01*percent_lightness; 3066 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue); 3067 } 3068 3069 static inline void ModulateHSV(const double percent_hue, 3070 const double percent_saturation,const double percent_value,double *red, 3071 double *green,double *blue) 3072 { 3073 double 3074 hue, 3075 saturation, 3076 value; 3077 3078 /* 3079 Increase or decrease color value, saturation, or hue. 3080 */ 3081 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value); 3082 hue+=0.5*(0.01*percent_hue-1.0); 3083 while (hue < 0.0) 3084 hue+=1.0; 3085 while (hue >= 1.0) 3086 hue-=1.0; 3087 saturation*=0.01*percent_saturation; 3088 value*=0.01*percent_value; 3089 ConvertHSVToRGB(hue,saturation,value,red,green,blue); 3090 } 3091 3092 static inline void ModulateHWB(const double percent_hue, 3093 const double percent_whiteness,const double percent_blackness,double *red, 3094 double *green,double *blue) 3095 { 3096 double 3097 blackness, 3098 hue, 3099 whiteness; 3100 3101 /* 3102 Increase or decrease color blackness, whiteness, or hue. 3103 */ 3104 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness); 3105 hue+=0.5*(0.01*percent_hue-1.0); 3106 while (hue < 0.0) 3107 hue+=1.0; 3108 while (hue >= 1.0) 3109 hue-=1.0; 3110 blackness*=0.01*percent_blackness; 3111 whiteness*=0.01*percent_whiteness; 3112 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue); 3113 } 3114 3115 static inline void ModulateLCHab(const double percent_luma, 3116 const double percent_chroma,const double percent_hue,double *red, 3117 double *green,double *blue) 3118 { 3119 double 3120 hue, 3121 luma, 3122 chroma; 3123 3124 /* 3125 Increase or decrease color luma, chroma, or hue. 3126 */ 3127 ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue); 3128 luma*=0.01*percent_luma; 3129 chroma*=0.01*percent_chroma; 3130 hue+=0.5*(0.01*percent_hue-1.0); 3131 while (hue < 0.0) 3132 hue+=1.0; 3133 while (hue >= 1.0) 3134 hue-=1.0; 3135 ConvertLCHabToRGB(luma,chroma,hue,red,green,blue); 3136 } 3137 3138 static inline void ModulateLCHuv(const double percent_luma, 3139 const double percent_chroma,const double percent_hue,double *red, 3140 double *green,double *blue) 3141 { 3142 double 3143 hue, 3144 luma, 3145 chroma; 3146 3147 /* 3148 Increase or decrease color luma, chroma, or hue. 3149 */ 3150 ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue); 3151 luma*=0.01*percent_luma; 3152 chroma*=0.01*percent_chroma; 3153 hue+=0.5*(0.01*percent_hue-1.0); 3154 while (hue < 0.0) 3155 hue+=1.0; 3156 while (hue >= 1.0) 3157 hue-=1.0; 3158 ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue); 3159 } 3160 3161 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate, 3162 ExceptionInfo *exception) 3163 { 3164 #define ModulateImageTag "Modulate/Image" 3165 3166 CacheView 3167 *image_view; 3168 3169 ColorspaceType 3170 colorspace; 3171 3172 const char 3173 *artifact; 3174 3175 double 3176 percent_brightness, 3177 percent_hue, 3178 percent_saturation; 3179 3180 GeometryInfo 3181 geometry_info; 3182 3183 MagickBooleanType 3184 status; 3185 3186 MagickOffsetType 3187 progress; 3188 3189 MagickStatusType 3190 flags; 3191 3192 register ssize_t 3193 i; 3194 3195 ssize_t 3196 y; 3197 3198 /* 3199 Initialize modulate table. 3200 */ 3201 assert(image != (Image *) NULL); 3202 assert(image->signature == MagickCoreSignature); 3203 if (image->debug != MagickFalse) 3204 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3205 if (modulate == (char *) NULL) 3206 return(MagickFalse); 3207 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 3208 (void) SetImageColorspace(image,sRGBColorspace,exception); 3209 flags=ParseGeometry(modulate,&geometry_info); 3210 percent_brightness=geometry_info.rho; 3211 percent_saturation=geometry_info.sigma; 3212 if ((flags & SigmaValue) == 0) 3213 percent_saturation=100.0; 3214 percent_hue=geometry_info.xi; 3215 if ((flags & XiValue) == 0) 3216 percent_hue=100.0; 3217 colorspace=UndefinedColorspace; 3218 artifact=GetImageArtifact(image,"modulate:colorspace"); 3219 if (artifact != (const char *) NULL) 3220 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions, 3221 MagickFalse,artifact); 3222 if (image->storage_class == PseudoClass) 3223 for (i=0; i < (ssize_t) image->colors; i++) 3224 { 3225 double 3226 blue, 3227 green, 3228 red; 3229 3230 /* 3231 Modulate image colormap. 3232 */ 3233 red=(double) image->colormap[i].red; 3234 green=(double) image->colormap[i].green; 3235 blue=(double) image->colormap[i].blue; 3236 switch (colorspace) 3237 { 3238 case HCLColorspace: 3239 { 3240 ModulateHCL(percent_hue,percent_saturation,percent_brightness, 3241 &red,&green,&blue); 3242 break; 3243 } 3244 case HCLpColorspace: 3245 { 3246 ModulateHCLp(percent_hue,percent_saturation,percent_brightness, 3247 &red,&green,&blue); 3248 break; 3249 } 3250 case HSBColorspace: 3251 { 3252 ModulateHSB(percent_hue,percent_saturation,percent_brightness, 3253 &red,&green,&blue); 3254 break; 3255 } 3256 case HSIColorspace: 3257 { 3258 ModulateHSI(percent_hue,percent_saturation,percent_brightness, 3259 &red,&green,&blue); 3260 break; 3261 } 3262 case HSLColorspace: 3263 default: 3264 { 3265 ModulateHSL(percent_hue,percent_saturation,percent_brightness, 3266 &red,&green,&blue); 3267 break; 3268 } 3269 case HSVColorspace: 3270 { 3271 ModulateHSV(percent_hue,percent_saturation,percent_brightness, 3272 &red,&green,&blue); 3273 break; 3274 } 3275 case HWBColorspace: 3276 { 3277 ModulateHWB(percent_hue,percent_saturation,percent_brightness, 3278 &red,&green,&blue); 3279 break; 3280 } 3281 case LCHColorspace: 3282 case LCHabColorspace: 3283 { 3284 ModulateLCHab(percent_brightness,percent_saturation,percent_hue, 3285 &red,&green,&blue); 3286 break; 3287 } 3288 case LCHuvColorspace: 3289 { 3290 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue, 3291 &red,&green,&blue); 3292 break; 3293 } 3294 } 3295 image->colormap[i].red=red; 3296 image->colormap[i].green=green; 3297 image->colormap[i].blue=blue; 3298 } 3299 /* 3300 Modulate image. 3301 */ 3302 #if defined(MAGICKCORE_OPENCL_SUPPORT) 3303 if (AccelerateModulateImage(image,percent_brightness,percent_hue, 3304 percent_saturation,colorspace,exception) != MagickFalse) 3305 return(MagickTrue); 3306 #endif 3307 status=MagickTrue; 3308 progress=0; 3309 image_view=AcquireAuthenticCacheView(image,exception); 3310 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3311 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 3312 magick_threads(image,image,image->rows,1) 3313 #endif 3314 for (y=0; y < (ssize_t) image->rows; y++) 3315 { 3316 register Quantum 3317 *magick_restrict q; 3318 3319 register ssize_t 3320 x; 3321 3322 if (status == MagickFalse) 3323 continue; 3324 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 3325 if (q == (Quantum *) NULL) 3326 { 3327 status=MagickFalse; 3328 continue; 3329 } 3330 for (x=0; x < (ssize_t) image->columns; x++) 3331 { 3332 double 3333 blue, 3334 green, 3335 red; 3336 3337 red=(double) GetPixelRed(image,q); 3338 green=(double) GetPixelGreen(image,q); 3339 blue=(double) GetPixelBlue(image,q); 3340 switch (colorspace) 3341 { 3342 case HCLColorspace: 3343 { 3344 ModulateHCL(percent_hue,percent_saturation,percent_brightness, 3345 &red,&green,&blue); 3346 break; 3347 } 3348 case HCLpColorspace: 3349 { 3350 ModulateHCLp(percent_hue,percent_saturation,percent_brightness, 3351 &red,&green,&blue); 3352 break; 3353 } 3354 case HSBColorspace: 3355 { 3356 ModulateHSB(percent_hue,percent_saturation,percent_brightness, 3357 &red,&green,&blue); 3358 break; 3359 } 3360 case HSLColorspace: 3361 default: 3362 { 3363 ModulateHSL(percent_hue,percent_saturation,percent_brightness, 3364 &red,&green,&blue); 3365 break; 3366 } 3367 case HSVColorspace: 3368 { 3369 ModulateHSV(percent_hue,percent_saturation,percent_brightness, 3370 &red,&green,&blue); 3371 break; 3372 } 3373 case HWBColorspace: 3374 { 3375 ModulateHWB(percent_hue,percent_saturation,percent_brightness, 3376 &red,&green,&blue); 3377 break; 3378 } 3379 case LCHabColorspace: 3380 { 3381 ModulateLCHab(percent_brightness,percent_saturation,percent_hue, 3382 &red,&green,&blue); 3383 break; 3384 } 3385 case LCHColorspace: 3386 case LCHuvColorspace: 3387 { 3388 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue, 3389 &red,&green,&blue); 3390 break; 3391 } 3392 } 3393 SetPixelRed(image,ClampToQuantum(red),q); 3394 SetPixelGreen(image,ClampToQuantum(green),q); 3395 SetPixelBlue(image,ClampToQuantum(blue),q); 3396 q+=GetPixelChannels(image); 3397 } 3398 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 3399 status=MagickFalse; 3400 if (image->progress_monitor != (MagickProgressMonitor) NULL) 3401 { 3402 MagickBooleanType 3403 proceed; 3404 3405 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3406 #pragma omp critical (MagickCore_ModulateImage) 3407 #endif 3408 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows); 3409 if (proceed == MagickFalse) 3410 status=MagickFalse; 3411 } 3412 } 3413 image_view=DestroyCacheView(image_view); 3414 return(status); 3415 } 3416 3417 /* 3419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3420 % % 3421 % % 3422 % % 3423 % N e g a t e I m a g e % 3424 % % 3425 % % 3426 % % 3427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3428 % 3429 % NegateImage() negates the colors in the reference image. The grayscale 3430 % option means that only grayscale values within the image are negated. 3431 % 3432 % The format of the NegateImage method is: 3433 % 3434 % MagickBooleanType NegateImage(Image *image, 3435 % const MagickBooleanType grayscale,ExceptionInfo *exception) 3436 % 3437 % A description of each parameter follows: 3438 % 3439 % o image: the image. 3440 % 3441 % o grayscale: If MagickTrue, only negate grayscale pixels within the image. 3442 % 3443 % o exception: return any errors or warnings in this structure. 3444 % 3445 */ 3446 MagickExport MagickBooleanType NegateImage(Image *image, 3447 const MagickBooleanType grayscale,ExceptionInfo *exception) 3448 { 3449 #define NegateImageTag "Negate/Image" 3450 3451 CacheView 3452 *image_view; 3453 3454 MagickBooleanType 3455 status; 3456 3457 MagickOffsetType 3458 progress; 3459 3460 register ssize_t 3461 i; 3462 3463 ssize_t 3464 y; 3465 3466 assert(image != (Image *) NULL); 3467 assert(image->signature == MagickCoreSignature); 3468 if (image->debug != MagickFalse) 3469 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3470 if (image->storage_class == PseudoClass) 3471 for (i=0; i < (ssize_t) image->colors; i++) 3472 { 3473 /* 3474 Negate colormap. 3475 */ 3476 if( grayscale != MagickFalse ) 3477 if ((image->colormap[i].red != image->colormap[i].green) || 3478 (image->colormap[i].green != image->colormap[i].blue)) 3479 continue; 3480 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 3481 image->colormap[i].red=QuantumRange-image->colormap[i].red; 3482 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 3483 image->colormap[i].green=QuantumRange-image->colormap[i].green; 3484 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 3485 image->colormap[i].blue=QuantumRange-image->colormap[i].blue; 3486 } 3487 /* 3488 Negate image. 3489 */ 3490 status=MagickTrue; 3491 progress=0; 3492 image_view=AcquireAuthenticCacheView(image,exception); 3493 if( grayscale != MagickFalse ) 3494 { 3495 for (y=0; y < (ssize_t) image->rows; y++) 3496 { 3497 MagickBooleanType 3498 sync; 3499 3500 register Quantum 3501 *magick_restrict q; 3502 3503 register ssize_t 3504 x; 3505 3506 if (status == MagickFalse) 3507 continue; 3508 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 3509 exception); 3510 if (q == (Quantum *) NULL) 3511 { 3512 status=MagickFalse; 3513 continue; 3514 } 3515 for (x=0; x < (ssize_t) image->columns; x++) 3516 { 3517 register ssize_t 3518 j; 3519 3520 if ((GetPixelReadMask(image,q) == 0) || 3521 IsPixelGray(image,q) != MagickFalse) 3522 { 3523 q+=GetPixelChannels(image); 3524 continue; 3525 } 3526 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 3527 { 3528 PixelChannel channel=GetPixelChannelChannel(image,j); 3529 PixelTrait traits=GetPixelChannelTraits(image,channel); 3530 if ((traits & UpdatePixelTrait) == 0) 3531 continue; 3532 q[j]=QuantumRange-q[j]; 3533 } 3534 q+=GetPixelChannels(image); 3535 } 3536 sync=SyncCacheViewAuthenticPixels(image_view,exception); 3537 if (sync == MagickFalse) 3538 status=MagickFalse; 3539 if (image->progress_monitor != (MagickProgressMonitor) NULL) 3540 { 3541 MagickBooleanType 3542 proceed; 3543 3544 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3545 #pragma omp critical (MagickCore_NegateImage) 3546 #endif 3547 proceed=SetImageProgress(image,NegateImageTag,progress++, 3548 image->rows); 3549 if (proceed == MagickFalse) 3550 status=MagickFalse; 3551 } 3552 } 3553 image_view=DestroyCacheView(image_view); 3554 return(MagickTrue); 3555 } 3556 /* 3557 Negate image. 3558 */ 3559 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3560 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 3561 magick_threads(image,image,image->rows,1) 3562 #endif 3563 for (y=0; y < (ssize_t) image->rows; y++) 3564 { 3565 register Quantum 3566 *magick_restrict q; 3567 3568 register ssize_t 3569 x; 3570 3571 if (status == MagickFalse) 3572 continue; 3573 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 3574 if (q == (Quantum *) NULL) 3575 { 3576 status=MagickFalse; 3577 continue; 3578 } 3579 for (x=0; x < (ssize_t) image->columns; x++) 3580 { 3581 register ssize_t 3582 j; 3583 3584 if (GetPixelReadMask(image,q) == 0) 3585 { 3586 q+=GetPixelChannels(image); 3587 continue; 3588 } 3589 for (j=0; j < (ssize_t) GetPixelChannels(image); j++) 3590 { 3591 PixelChannel channel=GetPixelChannelChannel(image,j); 3592 PixelTrait traits=GetPixelChannelTraits(image,channel); 3593 if ((traits & UpdatePixelTrait) == 0) 3594 continue; 3595 q[j]=QuantumRange-q[j]; 3596 } 3597 q+=GetPixelChannels(image); 3598 } 3599 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 3600 status=MagickFalse; 3601 if (image->progress_monitor != (MagickProgressMonitor) NULL) 3602 { 3603 MagickBooleanType 3604 proceed; 3605 3606 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3607 #pragma omp critical (MagickCore_NegateImage) 3608 #endif 3609 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows); 3610 if (proceed == MagickFalse) 3611 status=MagickFalse; 3612 } 3613 } 3614 image_view=DestroyCacheView(image_view); 3615 return(status); 3616 } 3617 3618 /* 3620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3621 % % 3622 % % 3623 % % 3624 % N o r m a l i z e I m a g e % 3625 % % 3626 % % 3627 % % 3628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3629 % 3630 % The NormalizeImage() method enhances the contrast of a color image by 3631 % mapping the darkest 2 percent of all pixel to black and the brightest 3632 % 1 percent to white. 3633 % 3634 % The format of the NormalizeImage method is: 3635 % 3636 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception) 3637 % 3638 % A description of each parameter follows: 3639 % 3640 % o image: the image. 3641 % 3642 % o exception: return any errors or warnings in this structure. 3643 % 3644 */ 3645 MagickExport MagickBooleanType NormalizeImage(Image *image, 3646 ExceptionInfo *exception) 3647 { 3648 double 3649 black_point, 3650 white_point; 3651 3652 black_point=(double) image->columns*image->rows*0.0015; 3653 white_point=(double) image->columns*image->rows*0.9995; 3654 return(ContrastStretchImage(image,black_point,white_point,exception)); 3655 } 3656 3657 /* 3659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3660 % % 3661 % % 3662 % % 3663 % S i g m o i d a l C o n t r a s t I m a g e % 3664 % % 3665 % % 3666 % % 3667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3668 % 3669 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear 3670 % sigmoidal contrast algorithm. Increase the contrast of the image using a 3671 % sigmoidal transfer function without saturating highlights or shadows. 3672 % Contrast indicates how much to increase the contrast (0 is none; 3 is 3673 % typical; 20 is pushing it); mid-point indicates where midtones fall in the 3674 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set 3675 % sharpen to MagickTrue to increase the image contrast otherwise the contrast 3676 % is reduced. 3677 % 3678 % The format of the SigmoidalContrastImage method is: 3679 % 3680 % MagickBooleanType SigmoidalContrastImage(Image *image, 3681 % const MagickBooleanType sharpen,const char *levels, 3682 % ExceptionInfo *exception) 3683 % 3684 % A description of each parameter follows: 3685 % 3686 % o image: the image. 3687 % 3688 % o sharpen: Increase or decrease image contrast. 3689 % 3690 % o contrast: strength of the contrast, the larger the number the more 3691 % 'threshold-like' it becomes. 3692 % 3693 % o midpoint: midpoint of the function as a color value 0 to QuantumRange. 3694 % 3695 % o exception: return any errors or warnings in this structure. 3696 % 3697 */ 3698 3699 /* 3700 ImageMagick 6 has a version of this function which uses LUTs. 3701 */ 3702 3703 /* 3704 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope 3705 constant" set to a. 3706 3707 The first version, based on the hyperbolic tangent tanh, when combined with 3708 the scaling step, is an exact arithmetic clone of the the sigmoid function 3709 based on the logistic curve. The equivalence is based on the identity 3710 3711 1/(1+exp(-t)) = (1+tanh(t/2))/2 3712 3713 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the 3714 scaled sigmoidal derivation is invariant under affine transformations of 3715 the ordinate. 3716 3717 The tanh version is almost certainly more accurate and cheaper. The 0.5 3718 factor in the argument is to clone the legacy ImageMagick behavior. The 3719 reason for making the define depend on atanh even though it only uses tanh 3720 has to do with the construction of the inverse of the scaled sigmoidal. 3721 */ 3722 #if defined(MAGICKCORE_HAVE_ATANH) 3723 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) ) 3724 #else 3725 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) ) 3726 #endif 3727 /* 3728 Scaled sigmoidal function: 3729 3730 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) / 3731 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) ) 3732 3733 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and 3734 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit 3735 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by 3736 zero. This is fixed below by exiting immediately when contrast is small, 3737 leaving the image (or colormap) unmodified. This appears to be safe because 3738 the series expansion of the logistic sigmoidal function around x=b is 3739 3740 1/2-a*(b-x)/4+... 3741 3742 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh). 3743 */ 3744 #define ScaledSigmoidal(a,b,x) ( \ 3745 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \ 3746 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) ) 3747 /* 3748 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b 3749 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic 3750 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even 3751 when creating a LUT from in gamut values, hence the branching. In 3752 addition, HDRI may have out of gamut values. 3753 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal: 3754 It is only a right inverse. This is unavoidable. 3755 */ 3756 static inline double InverseScaledSigmoidal(const double a,const double b, 3757 const double x) 3758 { 3759 const double sig0=Sigmoidal(a,b,0.0); 3760 const double sig1=Sigmoidal(a,b,1.0); 3761 const double argument=(sig1-sig0)*x+sig0; 3762 const double clamped= 3763 ( 3764 #if defined(MAGICKCORE_HAVE_ATANH) 3765 argument < -1+MagickEpsilon 3766 ? 3767 -1+MagickEpsilon 3768 : 3769 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument ) 3770 ); 3771 return(b+(2.0/a)*atanh(clamped)); 3772 #else 3773 argument < MagickEpsilon 3774 ? 3775 MagickEpsilon 3776 : 3777 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument ) 3778 ); 3779 return(b-log(1.0/clamped-1.0)/a); 3780 #endif 3781 } 3782 3783 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image, 3784 const MagickBooleanType sharpen,const double contrast,const double midpoint, 3785 ExceptionInfo *exception) 3786 { 3787 #define SigmoidalContrastImageTag "SigmoidalContrast/Image" 3788 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \ 3789 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) ) 3790 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \ 3791 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) ) 3792 3793 CacheView 3794 *image_view; 3795 3796 MagickBooleanType 3797 status; 3798 3799 MagickOffsetType 3800 progress; 3801 3802 ssize_t 3803 y; 3804 3805 /* 3806 Convenience macros. 3807 */ 3808 assert(image != (Image *) NULL); 3809 assert(image->signature == MagickCoreSignature); 3810 if (image->debug != MagickFalse) 3811 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3812 /* 3813 Side effect: may clamp values unless contrast<MagickEpsilon, in which 3814 case nothing is done. 3815 */ 3816 if (contrast < MagickEpsilon) 3817 return(MagickTrue); 3818 /* 3819 Sigmoidal-contrast enhance colormap. 3820 */ 3821 if (image->storage_class == PseudoClass) 3822 { 3823 register ssize_t 3824 i; 3825 3826 if( sharpen != MagickFalse ) 3827 for (i=0; i < (ssize_t) image->colors; i++) 3828 { 3829 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 3830 image->colormap[i].red=(MagickRealType) ScaledSig( 3831 image->colormap[i].red); 3832 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 3833 image->colormap[i].green=(MagickRealType) ScaledSig( 3834 image->colormap[i].green); 3835 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 3836 image->colormap[i].blue=(MagickRealType) ScaledSig( 3837 image->colormap[i].blue); 3838 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 3839 image->colormap[i].alpha=(MagickRealType) ScaledSig( 3840 image->colormap[i].alpha); 3841 } 3842 else 3843 for (i=0; i < (ssize_t) image->colors; i++) 3844 { 3845 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 3846 image->colormap[i].red=(MagickRealType) InverseScaledSig( 3847 image->colormap[i].red); 3848 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 3849 image->colormap[i].green=(MagickRealType) InverseScaledSig( 3850 image->colormap[i].green); 3851 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 3852 image->colormap[i].blue=(MagickRealType) InverseScaledSig( 3853 image->colormap[i].blue); 3854 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 3855 image->colormap[i].alpha=(MagickRealType) InverseScaledSig( 3856 image->colormap[i].alpha); 3857 } 3858 } 3859 /* 3860 Sigmoidal-contrast enhance image. 3861 */ 3862 status=MagickTrue; 3863 progress=0; 3864 image_view=AcquireAuthenticCacheView(image,exception); 3865 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3866 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 3867 magick_threads(image,image,image->rows,1) 3868 #endif 3869 for (y=0; y < (ssize_t) image->rows; y++) 3870 { 3871 register Quantum 3872 *magick_restrict q; 3873 3874 register ssize_t 3875 x; 3876 3877 if (status == MagickFalse) 3878 continue; 3879 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 3880 if (q == (Quantum *) NULL) 3881 { 3882 status=MagickFalse; 3883 continue; 3884 } 3885 for (x=0; x < (ssize_t) image->columns; x++) 3886 { 3887 register ssize_t 3888 i; 3889 3890 if (GetPixelReadMask(image,q) == 0) 3891 { 3892 q+=GetPixelChannels(image); 3893 continue; 3894 } 3895 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 3896 { 3897 PixelChannel channel=GetPixelChannelChannel(image,i); 3898 PixelTrait traits=GetPixelChannelTraits(image,channel); 3899 if ((traits & UpdatePixelTrait) == 0) 3900 continue; 3901 if( sharpen != MagickFalse ) 3902 q[i]=ScaledSig(q[i]); 3903 else 3904 q[i]=InverseScaledSig(q[i]); 3905 } 3906 q+=GetPixelChannels(image); 3907 } 3908 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 3909 status=MagickFalse; 3910 if (image->progress_monitor != (MagickProgressMonitor) NULL) 3911 { 3912 MagickBooleanType 3913 proceed; 3914 3915 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3916 #pragma omp critical (MagickCore_SigmoidalContrastImage) 3917 #endif 3918 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++, 3919 image->rows); 3920 if (proceed == MagickFalse) 3921 status=MagickFalse; 3922 } 3923 } 3924 image_view=DestroyCacheView(image_view); 3925 return(status); 3926 } 3927