|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % EEEEE N N H H AAA N N CCCC EEEEE % 00007 % E NN N H H A A NN N C E % 00008 % EEE N N N HHHHH AAAAA N N N C EEE % 00009 % E N NN H H A A N NN C E % 00010 % EEEEE N N H H A A N N CCCC EEEEE % 00011 % % 00012 % % 00013 % MagickCore Image Enhancement Methods % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 % 00038 */ 00039 00040 /* 00041 Include declarations. 00042 */ 00043 #include "MagickCore/studio.h" 00044 #include "MagickCore/artifact.h" 00045 #include "MagickCore/cache.h" 00046 #include "MagickCore/cache-view.h" 00047 #include "MagickCore/color.h" 00048 #include "MagickCore/color-private.h" 00049 #include "MagickCore/colorspace.h" 00050 #include "MagickCore/composite-private.h" 00051 #include "MagickCore/enhance.h" 00052 #include "MagickCore/exception.h" 00053 #include "MagickCore/exception-private.h" 00054 #include "MagickCore/fx.h" 00055 #include "MagickCore/gem.h" 00056 #include "MagickCore/gem-private.h" 00057 #include "MagickCore/geometry.h" 00058 #include "MagickCore/histogram.h" 00059 #include "MagickCore/image.h" 00060 #include "MagickCore/image-private.h" 00061 #include "MagickCore/memory_.h" 00062 #include "MagickCore/monitor.h" 00063 #include "MagickCore/monitor-private.h" 00064 #include "MagickCore/option.h" 00065 #include "MagickCore/pixel-accessor.h" 00066 #include "MagickCore/quantum.h" 00067 #include "MagickCore/quantum-private.h" 00068 #include "MagickCore/resample.h" 00069 #include "MagickCore/resample-private.h" 00070 #include "MagickCore/statistic.h" 00071 #include "MagickCore/string_.h" 00072 #include "MagickCore/string-private.h" 00073 #include "MagickCore/thread-private.h" 00074 #include "MagickCore/token.h" 00075 #include "MagickCore/xml-tree.h" 00076 #include "MagickCore/xml-tree-private.h" 00077 00078 /* 00079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00080 % % 00081 % % 00082 % % 00083 % A u t o G a m m a I m a g e % 00084 % % 00085 % % 00086 % % 00087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00088 % 00089 % AutoGammaImage() extract the 'mean' from the image and adjust the image 00090 % to try make set its gamma appropriatally. 00091 % 00092 % The format of the AutoGammaImage method is: 00093 % 00094 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception) 00095 % 00096 % A description of each parameter follows: 00097 % 00098 % o image: The image to auto-level 00099 % 00100 % o exception: return any errors or warnings in this structure. 00101 % 00102 */ 00103 MagickExport MagickBooleanType AutoGammaImage(Image *image, 00104 ExceptionInfo *exception) 00105 { 00106 double 00107 gamma, 00108 log_mean, 00109 mean, 00110 sans; 00111 00112 MagickStatusType 00113 status; 00114 00115 register ssize_t 00116 i; 00117 00118 log_mean=log(0.5); 00119 if (image->channel_mask == DefaultChannels) 00120 { 00121 /* 00122 Apply gamma correction equally across all given channels. 00123 */ 00124 (void) GetImageMean(image,&mean,&sans,exception); 00125 gamma=log(mean*QuantumScale)/log_mean; 00126 return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception)); 00127 } 00128 /* 00129 Auto-gamma each channel separately. 00130 */ 00131 status=MagickTrue; 00132 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 00133 { 00134 ChannelType 00135 channel_mask; 00136 00137 PixelChannel 00138 channel; 00139 00140 PixelTrait 00141 traits; 00142 00143 channel=GetPixelChannelMapChannel(image,i); 00144 traits=GetPixelChannelMapTraits(image,channel); 00145 if ((traits & UpdatePixelTrait) == 0) 00146 continue; 00147 channel_mask=SetPixelChannelMask(image,(ChannelType) (1 << i)); 00148 status=GetImageMean(image,&mean,&sans,exception); 00149 gamma=log(mean*QuantumScale)/log_mean; 00150 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception); 00151 (void) SetPixelChannelMask(image,channel_mask); 00152 if (status == MagickFalse) 00153 break; 00154 } 00155 return(status != 0 ? MagickTrue : MagickFalse); 00156 } 00157 00158 /* 00159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00160 % % 00161 % % 00162 % % 00163 % A u t o L e v e l I m a g e % 00164 % % 00165 % % 00166 % % 00167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00168 % 00169 % AutoLevelImage() adjusts the levels of a particular image channel by 00170 % scaling the minimum and maximum values to the full quantum range. 00171 % 00172 % The format of the LevelImage method is: 00173 % 00174 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception) 00175 % 00176 % A description of each parameter follows: 00177 % 00178 % o image: The image to auto-level 00179 % 00180 % o exception: return any errors or warnings in this structure. 00181 % 00182 */ 00183 MagickExport MagickBooleanType AutoLevelImage(Image *image, 00184 ExceptionInfo *exception) 00185 { 00186 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception)); 00187 } 00188 00189 /* 00190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00191 % % 00192 % % 00193 % % 00194 % B r i g h t n e s s C o n t r a s t I m a g e % 00195 % % 00196 % % 00197 % % 00198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00199 % 00200 % BrightnessContrastImage() changes the brightness and/or contrast of an 00201 % image. It converts the brightness and contrast parameters into slope and 00202 % intercept and calls a polynomical function to apply to the image. 00203 % 00204 % The format of the BrightnessContrastImage method is: 00205 % 00206 % MagickBooleanType BrightnessContrastImage(Image *image, 00207 % const double brightness,const double contrast,ExceptionInfo *exception) 00208 % 00209 % A description of each parameter follows: 00210 % 00211 % o image: the image. 00212 % 00213 % o brightness: the brightness percent (-100 .. 100). 00214 % 00215 % o contrast: the contrast percent (-100 .. 100). 00216 % 00217 % o exception: return any errors or warnings in this structure. 00218 % 00219 */ 00220 MagickExport MagickBooleanType BrightnessContrastImage(Image *image, 00221 const double brightness,const double contrast,ExceptionInfo *exception) 00222 { 00223 #define BrightnessContastImageTag "BrightnessContast/Image" 00224 00225 double 00226 alpha, 00227 coefficients[2], 00228 intercept, 00229 slope; 00230 00231 MagickBooleanType 00232 status; 00233 00234 /* 00235 Compute slope and intercept. 00236 */ 00237 assert(image != (Image *) NULL); 00238 assert(image->signature == MagickSignature); 00239 if (image->debug != MagickFalse) 00240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00241 alpha=contrast; 00242 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0)); 00243 if (slope < 0.0) 00244 slope=0.0; 00245 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope); 00246 coefficients[0]=slope; 00247 coefficients[1]=intercept; 00248 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception); 00249 return(status); 00250 } 00251 00252 /* 00253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00254 % % 00255 % % 00256 % % 00257 % C l u t I m a g e % 00258 % % 00259 % % 00260 % % 00261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00262 % 00263 % ClutImage() replaces each color value in the given image, by using it as an 00264 % index to lookup a replacement color value in a Color Look UP Table in the 00265 % form of an image. The values are extracted along a diagonal of the CLUT 00266 % image so either a horizontal or vertial gradient image can be used. 00267 % 00268 % Typically this is used to either re-color a gray-scale image according to a 00269 % color gradient in the CLUT image, or to perform a freeform histogram 00270 % (level) adjustment according to the (typically gray-scale) gradient in the 00271 % CLUT image. 00272 % 00273 % When the 'channel' mask includes the matte/alpha transparency channel but 00274 % one image has no such channel it is assumed that that image is a simple 00275 % gray-scale image that will effect the alpha channel values, either for 00276 % gray-scale coloring (with transparent or semi-transparent colors), or 00277 % a histogram adjustment of existing alpha channel values. If both images 00278 % have matte channels, direct and normal indexing is applied, which is rarely 00279 % used. 00280 % 00281 % The format of the ClutImage method is: 00282 % 00283 % MagickBooleanType ClutImage(Image *image,Image *clut_image, 00284 % const PixelInterpolateMethod method,ExceptionInfo *exception) 00285 % 00286 % A description of each parameter follows: 00287 % 00288 % o image: the image, which is replaced by indexed CLUT values 00289 % 00290 % o clut_image: the color lookup table image for replacement color values. 00291 % 00292 % o method: the pixel interpolation method. 00293 % 00294 % o exception: return any errors or warnings in this structure. 00295 % 00296 */ 00297 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image, 00298 const PixelInterpolateMethod method,ExceptionInfo *exception) 00299 { 00300 #define ClutImageTag "Clut/Image" 00301 00302 CacheView 00303 *clut_view, 00304 *image_view; 00305 00306 double 00307 *clut_map; 00308 00309 MagickBooleanType 00310 status; 00311 00312 MagickOffsetType 00313 progress; 00314 00315 register ssize_t 00316 x; 00317 00318 ssize_t 00319 adjust, 00320 y; 00321 00322 assert(image != (Image *) NULL); 00323 assert(image->signature == MagickSignature); 00324 if (image->debug != MagickFalse) 00325 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00326 assert(clut_image != (Image *) NULL); 00327 assert(clut_image->signature == MagickSignature); 00328 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 00329 return(MagickFalse); 00330 clut_map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)* 00331 sizeof(*clut_map)); 00332 if (clut_map == (double *) NULL) 00333 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00334 image->filename); 00335 /* 00336 Clut image. 00337 */ 00338 status=MagickTrue; 00339 progress=0; 00340 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1); 00341 clut_view=AcquireCacheView(clut_image); 00342 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00343 #pragma omp parallel for schedule(static,4) 00344 #endif 00345 for (x=0; x <= (ssize_t) MaxMap; x++) 00346 { 00347 register ssize_t 00348 i; 00349 00350 for (i=0; i < (ssize_t) GetPixelChannels(clut_image); i++) 00351 (void) InterpolatePixelChannel(clut_image,clut_view,(PixelChannel) i, 00352 method,QuantumScale*x*(clut_image->columns-adjust),QuantumScale*x* 00353 (clut_image->rows-adjust),clut_map+x*GetPixelChannels(clut_image)+i, 00354 exception); 00355 } 00356 clut_view=DestroyCacheView(clut_view); 00357 image_view=AcquireCacheView(image); 00358 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00359 #pragma omp parallel for schedule(static,4) shared(progress,status) 00360 #endif 00361 for (y=0; y < (ssize_t) image->rows; y++) 00362 { 00363 register Quantum 00364 *restrict q; 00365 00366 register ssize_t 00367 x; 00368 00369 if (status == MagickFalse) 00370 continue; 00371 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00372 if (q == (Quantum *) NULL) 00373 { 00374 status=MagickFalse; 00375 continue; 00376 } 00377 for (x=0; x < (ssize_t) image->columns; x++) 00378 { 00379 register ssize_t 00380 i; 00381 00382 if (GetPixelMask(image,q) != 0) 00383 { 00384 q+=GetPixelChannels(image); 00385 continue; 00386 } 00387 for (i=0; i < (ssize_t) GetPixelChannels(clut_image); i++) 00388 { 00389 PixelChannel 00390 channel; 00391 00392 PixelTrait 00393 clut_traits, 00394 traits; 00395 00396 channel=GetPixelChannelMapChannel(clut_image,i); 00397 clut_traits=GetPixelChannelMapTraits(clut_image,channel); 00398 traits=GetPixelChannelMapTraits(clut_image,channel); 00399 if ((traits == UndefinedPixelTrait) || 00400 (clut_traits == UndefinedPixelTrait) || 00401 ((traits & UpdatePixelTrait) == 0)) 00402 continue; 00403 SetPixelChannel(clut_image,channel,ClampToQuantum(clut_map[ 00404 ScaleQuantumToMap(GetPixelChannel(clut_image,channel,q))* 00405 GetPixelChannels(clut_image)+channel]),q); 00406 } 00407 q+=GetPixelChannels(image); 00408 } 00409 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00410 status=MagickFalse; 00411 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00412 { 00413 MagickBooleanType 00414 proceed; 00415 00416 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00417 #pragma omp critical (MagickCore_ClutImage) 00418 #endif 00419 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows); 00420 if (proceed == MagickFalse) 00421 status=MagickFalse; 00422 } 00423 } 00424 image_view=DestroyCacheView(image_view); 00425 clut_map=(double *) RelinquishMagickMemory(clut_map); 00426 if ((clut_image->matte != MagickFalse) && 00427 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)) 00428 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception); 00429 return(status); 00430 } 00431 00432 /* 00433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00434 % % 00435 % % 00436 % % 00437 % C o l o r D e c i s i o n L i s t I m a g e % 00438 % % 00439 % % 00440 % % 00441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00442 % 00443 % ColorDecisionListImage() accepts a lightweight Color Correction Collection 00444 % (CCC) file which solely contains one or more color corrections and applies 00445 % the correction to the image. Here is a sample CCC file: 00446 % 00447 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2"> 00448 % <ColorCorrection id="cc03345"> 00449 % <SOPNode> 00450 % <Slope> 0.9 1.2 0.5 </Slope> 00451 % <Offset> 0.4 -0.5 0.6 </Offset> 00452 % <Power> 1.0 0.8 1.5 </Power> 00453 % </SOPNode> 00454 % <SATNode> 00455 % <Saturation> 0.85 </Saturation> 00456 % </SATNode> 00457 % </ColorCorrection> 00458 % </ColorCorrectionCollection> 00459 % 00460 % which includes the slop, offset, and power for each of the RGB channels 00461 % as well as the saturation. 00462 % 00463 % The format of the ColorDecisionListImage method is: 00464 % 00465 % MagickBooleanType ColorDecisionListImage(Image *image, 00466 % const char *color_correction_collection,ExceptionInfo *exception) 00467 % 00468 % A description of each parameter follows: 00469 % 00470 % o image: the image. 00471 % 00472 % o color_correction_collection: the color correction collection in XML. 00473 % 00474 % o exception: return any errors or warnings in this structure. 00475 % 00476 */ 00477 MagickExport MagickBooleanType ColorDecisionListImage(Image *image, 00478 const char *color_correction_collection,ExceptionInfo *exception) 00479 { 00480 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image" 00481 00482 typedef struct _Correction 00483 { 00484 double 00485 slope, 00486 offset, 00487 power; 00488 } Correction; 00489 00490 typedef struct _ColorCorrection 00491 { 00492 Correction 00493 red, 00494 green, 00495 blue; 00496 00497 double 00498 saturation; 00499 } ColorCorrection; 00500 00501 CacheView 00502 *image_view; 00503 00504 char 00505 token[MaxTextExtent]; 00506 00507 ColorCorrection 00508 color_correction; 00509 00510 const char 00511 *content, 00512 *p; 00513 00514 MagickBooleanType 00515 status; 00516 00517 MagickOffsetType 00518 progress; 00519 00520 PixelInfo 00521 *cdl_map; 00522 00523 register ssize_t 00524 i; 00525 00526 ssize_t 00527 y; 00528 00529 XMLTreeInfo 00530 *cc, 00531 *ccc, 00532 *sat, 00533 *sop; 00534 00535 /* 00536 Allocate and initialize cdl maps. 00537 */ 00538 assert(image != (Image *) NULL); 00539 assert(image->signature == MagickSignature); 00540 if (image->debug != MagickFalse) 00541 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00542 if (color_correction_collection == (const char *) NULL) 00543 return(MagickFalse); 00544 ccc=NewXMLTree((const char *) color_correction_collection,exception); 00545 if (ccc == (XMLTreeInfo *) NULL) 00546 return(MagickFalse); 00547 cc=GetXMLTreeChild(ccc,"ColorCorrection"); 00548 if (cc == (XMLTreeInfo *) NULL) 00549 { 00550 ccc=DestroyXMLTree(ccc); 00551 return(MagickFalse); 00552 } 00553 color_correction.red.slope=1.0; 00554 color_correction.red.offset=0.0; 00555 color_correction.red.power=1.0; 00556 color_correction.green.slope=1.0; 00557 color_correction.green.offset=0.0; 00558 color_correction.green.power=1.0; 00559 color_correction.blue.slope=1.0; 00560 color_correction.blue.offset=0.0; 00561 color_correction.blue.power=1.0; 00562 color_correction.saturation=0.0; 00563 sop=GetXMLTreeChild(cc,"SOPNode"); 00564 if (sop != (XMLTreeInfo *) NULL) 00565 { 00566 XMLTreeInfo 00567 *offset, 00568 *power, 00569 *slope; 00570 00571 slope=GetXMLTreeChild(sop,"Slope"); 00572 if (slope != (XMLTreeInfo *) NULL) 00573 { 00574 content=GetXMLTreeContent(slope); 00575 p=(const char *) content; 00576 for (i=0; (*p != '\0') && (i < 3); i++) 00577 { 00578 GetMagickToken(p,&p,token); 00579 if (*token == ',') 00580 GetMagickToken(p,&p,token); 00581 switch (i) 00582 { 00583 case 0: 00584 { 00585 color_correction.red.slope=StringToDouble(token,(char **) NULL); 00586 break; 00587 } 00588 case 1: 00589 { 00590 color_correction.green.slope=StringToDouble(token, 00591 (char **) NULL); 00592 break; 00593 } 00594 case 2: 00595 { 00596 color_correction.blue.slope=StringToDouble(token, 00597 (char **) NULL); 00598 break; 00599 } 00600 } 00601 } 00602 } 00603 offset=GetXMLTreeChild(sop,"Offset"); 00604 if (offset != (XMLTreeInfo *) NULL) 00605 { 00606 content=GetXMLTreeContent(offset); 00607 p=(const char *) content; 00608 for (i=0; (*p != '\0') && (i < 3); i++) 00609 { 00610 GetMagickToken(p,&p,token); 00611 if (*token == ',') 00612 GetMagickToken(p,&p,token); 00613 switch (i) 00614 { 00615 case 0: 00616 { 00617 color_correction.red.offset=StringToDouble(token, 00618 (char **) NULL); 00619 break; 00620 } 00621 case 1: 00622 { 00623 color_correction.green.offset=StringToDouble(token, 00624 (char **) NULL); 00625 break; 00626 } 00627 case 2: 00628 { 00629 color_correction.blue.offset=StringToDouble(token, 00630 (char **) NULL); 00631 break; 00632 } 00633 } 00634 } 00635 } 00636 power=GetXMLTreeChild(sop,"Power"); 00637 if (power != (XMLTreeInfo *) NULL) 00638 { 00639 content=GetXMLTreeContent(power); 00640 p=(const char *) content; 00641 for (i=0; (*p != '\0') && (i < 3); i++) 00642 { 00643 GetMagickToken(p,&p,token); 00644 if (*token == ',') 00645 GetMagickToken(p,&p,token); 00646 switch (i) 00647 { 00648 case 0: 00649 { 00650 color_correction.red.power=StringToDouble(token,(char **) NULL); 00651 break; 00652 } 00653 case 1: 00654 { 00655 color_correction.green.power=StringToDouble(token, 00656 (char **) NULL); 00657 break; 00658 } 00659 case 2: 00660 { 00661 color_correction.blue.power=StringToDouble(token, 00662 (char **) NULL); 00663 break; 00664 } 00665 } 00666 } 00667 } 00668 } 00669 sat=GetXMLTreeChild(cc,"SATNode"); 00670 if (sat != (XMLTreeInfo *) NULL) 00671 { 00672 XMLTreeInfo 00673 *saturation; 00674 00675 saturation=GetXMLTreeChild(sat,"Saturation"); 00676 if (saturation != (XMLTreeInfo *) NULL) 00677 { 00678 content=GetXMLTreeContent(saturation); 00679 p=(const char *) content; 00680 GetMagickToken(p,&p,token); 00681 color_correction.saturation=StringToDouble(token,(char **) NULL); 00682 } 00683 } 00684 ccc=DestroyXMLTree(ccc); 00685 if (image->debug != MagickFalse) 00686 { 00687 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00688 " Color Correction Collection:"); 00689 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00690 " color_correction.red.slope: %g",color_correction.red.slope); 00691 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00692 " color_correction.red.offset: %g",color_correction.red.offset); 00693 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00694 " color_correction.red.power: %g",color_correction.red.power); 00695 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00696 " color_correction.green.slope: %g",color_correction.green.slope); 00697 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00698 " color_correction.green.offset: %g",color_correction.green.offset); 00699 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00700 " color_correction.green.power: %g",color_correction.green.power); 00701 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00702 " color_correction.blue.slope: %g",color_correction.blue.slope); 00703 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00704 " color_correction.blue.offset: %g",color_correction.blue.offset); 00705 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00706 " color_correction.blue.power: %g",color_correction.blue.power); 00707 (void) LogMagickEvent(TransformEvent,GetMagickModule(), 00708 " color_correction.saturation: %g",color_correction.saturation); 00709 } 00710 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map)); 00711 if (cdl_map == (PixelInfo *) NULL) 00712 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00713 image->filename); 00714 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00715 #pragma omp parallel for schedule(static,4) 00716 #endif 00717 for (i=0; i <= (ssize_t) MaxMap; i++) 00718 { 00719 cdl_map[i].red=(MagickRealType) ScaleMapToQuantum((MagickRealType) 00720 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+ 00721 color_correction.red.offset,color_correction.red.power)))); 00722 cdl_map[i].green=(MagickRealType) ScaleMapToQuantum((MagickRealType) 00723 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+ 00724 color_correction.green.offset,color_correction.green.power)))); 00725 cdl_map[i].blue=(MagickRealType) ScaleMapToQuantum((MagickRealType) 00726 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+ 00727 color_correction.blue.offset,color_correction.blue.power)))); 00728 } 00729 if (image->storage_class == PseudoClass) 00730 { 00731 /* 00732 Apply transfer function to colormap. 00733 */ 00734 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00735 #pragma omp parallel for schedule(static,4) shared(progress,status) 00736 #endif 00737 for (i=0; i < (ssize_t) image->colors; i++) 00738 { 00739 double 00740 luma; 00741 00742 luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+ 00743 0.0722*image->colormap[i].blue; 00744 image->colormap[i].red=luma+color_correction.saturation*cdl_map[ 00745 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red- 00746 luma; 00747 image->colormap[i].green=luma+color_correction.saturation*cdl_map[ 00748 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green- 00749 luma; 00750 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[ 00751 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue- 00752 luma; 00753 } 00754 } 00755 /* 00756 Apply transfer function to image. 00757 */ 00758 status=MagickTrue; 00759 progress=0; 00760 image_view=AcquireCacheView(image); 00761 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00762 #pragma omp parallel for schedule(static,4) shared(progress,status) 00763 #endif 00764 for (y=0; y < (ssize_t) image->rows; y++) 00765 { 00766 double 00767 luma; 00768 00769 register Quantum 00770 *restrict q; 00771 00772 register ssize_t 00773 x; 00774 00775 if (status == MagickFalse) 00776 continue; 00777 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00778 if (q == (Quantum *) NULL) 00779 { 00780 status=MagickFalse; 00781 continue; 00782 } 00783 for (x=0; x < (ssize_t) image->columns; x++) 00784 { 00785 luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722* 00786 GetPixelBlue(image,q); 00787 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation* 00788 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q); 00789 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation* 00790 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q); 00791 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation* 00792 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q); 00793 q+=GetPixelChannels(image); 00794 } 00795 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00796 status=MagickFalse; 00797 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00798 { 00799 MagickBooleanType 00800 proceed; 00801 00802 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00803 #pragma omp critical (MagickCore_ColorDecisionListImageChannel) 00804 #endif 00805 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag, 00806 progress++,image->rows); 00807 if (proceed == MagickFalse) 00808 status=MagickFalse; 00809 } 00810 } 00811 image_view=DestroyCacheView(image_view); 00812 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map); 00813 return(status); 00814 } 00815 00816 /* 00817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00818 % % 00819 % % 00820 % % 00821 % C o n t r a s t I m a g e % 00822 % % 00823 % % 00824 % % 00825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00826 % 00827 % ContrastImage() enhances the intensity differences between the lighter and 00828 % darker elements of the image. Set sharpen to a MagickTrue to increase the 00829 % image contrast otherwise the contrast is reduced. 00830 % 00831 % The format of the ContrastImage method is: 00832 % 00833 % MagickBooleanType ContrastImage(Image *image, 00834 % const MagickBooleanType sharpen,ExceptionInfo *exception) 00835 % 00836 % A description of each parameter follows: 00837 % 00838 % o image: the image. 00839 % 00840 % o sharpen: Increase or decrease image contrast. 00841 % 00842 % o exception: return any errors or warnings in this structure. 00843 % 00844 */ 00845 00846 static void Contrast(const int sign,double *red,double *green,double *blue) 00847 { 00848 double 00849 brightness, 00850 hue, 00851 saturation; 00852 00853 /* 00854 Enhance contrast: dark color become darker, light color become lighter. 00855 */ 00856 assert(red != (double *) NULL); 00857 assert(green != (double *) NULL); 00858 assert(blue != (double *) NULL); 00859 hue=0.0; 00860 saturation=0.0; 00861 brightness=0.0; 00862 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness); 00863 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)- 00864 brightness); 00865 if (brightness > 1.0) 00866 brightness=1.0; 00867 else 00868 if (brightness < 0.0) 00869 brightness=0.0; 00870 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue); 00871 } 00872 00873 MagickExport MagickBooleanType ContrastImage(Image *image, 00874 const MagickBooleanType sharpen,ExceptionInfo *exception) 00875 { 00876 #define ContrastImageTag "Contrast/Image" 00877 00878 CacheView 00879 *image_view; 00880 00881 int 00882 sign; 00883 00884 MagickBooleanType 00885 status; 00886 00887 MagickOffsetType 00888 progress; 00889 00890 register ssize_t 00891 i; 00892 00893 ssize_t 00894 y; 00895 00896 assert(image != (Image *) NULL); 00897 assert(image->signature == MagickSignature); 00898 if (image->debug != MagickFalse) 00899 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00900 sign=sharpen != MagickFalse ? 1 : -1; 00901 if (image->storage_class == PseudoClass) 00902 { 00903 /* 00904 Contrast enhance colormap. 00905 */ 00906 for (i=0; i < (ssize_t) image->colors; i++) 00907 Contrast(sign,&image->colormap[i].red,&image->colormap[i].green, 00908 &image->colormap[i].blue); 00909 } 00910 /* 00911 Contrast enhance image. 00912 */ 00913 status=MagickTrue; 00914 progress=0; 00915 image_view=AcquireCacheView(image); 00916 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00917 #pragma omp parallel for schedule(static,4) shared(progress,status) 00918 #endif 00919 for (y=0; y < (ssize_t) image->rows; y++) 00920 { 00921 double 00922 blue, 00923 green, 00924 red; 00925 00926 register Quantum 00927 *restrict q; 00928 00929 register ssize_t 00930 x; 00931 00932 if (status == MagickFalse) 00933 continue; 00934 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00935 if (q == (Quantum *) NULL) 00936 { 00937 status=MagickFalse; 00938 continue; 00939 } 00940 for (x=0; x < (ssize_t) image->columns; x++) 00941 { 00942 red=(double) GetPixelRed(image,q); 00943 green=(double) GetPixelGreen(image,q); 00944 blue=(double) GetPixelBlue(image,q); 00945 Contrast(sign,&red,&green,&blue); 00946 SetPixelRed(image,ClampToQuantum(red),q); 00947 SetPixelGreen(image,ClampToQuantum(green),q); 00948 SetPixelBlue(image,ClampToQuantum(blue),q); 00949 q+=GetPixelChannels(image); 00950 } 00951 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00952 status=MagickFalse; 00953 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00954 { 00955 MagickBooleanType 00956 proceed; 00957 00958 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00959 #pragma omp critical (MagickCore_ContrastImage) 00960 #endif 00961 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows); 00962 if (proceed == MagickFalse) 00963 status=MagickFalse; 00964 } 00965 } 00966 image_view=DestroyCacheView(image_view); 00967 return(status); 00968 } 00969 00970 /* 00971 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00972 % % 00973 % % 00974 % % 00975 % C o n t r a s t S t r e t c h I m a g e % 00976 % % 00977 % % 00978 % % 00979 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00980 % 00981 % ContrastStretchImage() is a simple image enhancement technique that attempts 00982 % to improve the contrast in an image by `stretching' the range of intensity 00983 % values it contains to span a desired range of values. It differs from the 00984 % more sophisticated histogram equalization in that it can only apply a 00985 % linear scaling function to the image pixel values. As a result the 00986 % `enhancement' is less harsh. 00987 % 00988 % The format of the ContrastStretchImage method is: 00989 % 00990 % MagickBooleanType ContrastStretchImage(Image *image, 00991 % const char *levels,ExceptionInfo *exception) 00992 % 00993 % A description of each parameter follows: 00994 % 00995 % o image: the image. 00996 % 00997 % o black_point: the black point. 00998 % 00999 % o white_point: the white point. 01000 % 01001 % o levels: Specify the levels where the black and white points have the 01002 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.). 01003 % 01004 % o exception: return any errors or warnings in this structure. 01005 % 01006 */ 01007 MagickExport MagickBooleanType ContrastStretchImage(Image *image, 01008 const double black_point,const double white_point,ExceptionInfo *exception) 01009 { 01010 #define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color))) 01011 #define ContrastStretchImageTag "ContrastStretch/Image" 01012 01013 CacheView 01014 *image_view; 01015 01016 MagickBooleanType 01017 status; 01018 01019 MagickOffsetType 01020 progress; 01021 01022 double 01023 *black, 01024 *histogram, 01025 *stretch_map, 01026 *white; 01027 01028 register ssize_t 01029 i; 01030 01031 size_t 01032 number_channels; 01033 01034 ssize_t 01035 y; 01036 01037 /* 01038 Allocate histogram and stretch map. 01039 */ 01040 assert(image != (Image *) NULL); 01041 assert(image->signature == MagickSignature); 01042 if (image->debug != MagickFalse) 01043 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01044 black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black)); 01045 white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white)); 01046 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)* 01047 sizeof(*histogram)); 01048 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL, 01049 GetPixelChannels(image)*sizeof(*stretch_map)); 01050 if ((black == (double *) NULL) || (white == (double *) NULL) || 01051 (histogram == (double *) NULL) || (stretch_map == (double *) NULL)) 01052 { 01053 if (stretch_map != (double *) NULL) 01054 stretch_map=(double *) RelinquishMagickMemory(stretch_map); 01055 if (histogram != (double *) NULL) 01056 histogram=(double *) RelinquishMagickMemory(histogram); 01057 if (white != (double *) NULL) 01058 white=(double *) RelinquishMagickMemory(white); 01059 if (black != (double *) NULL) 01060 black=(double *) RelinquishMagickMemory(black); 01061 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01062 image->filename); 01063 } 01064 /* 01065 Form histogram. 01066 */ 01067 status=MagickTrue; 01068 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)* 01069 sizeof(*histogram)); 01070 image_view=AcquireCacheView(image); 01071 for (y=0; y < (ssize_t) image->rows; y++) 01072 { 01073 register const Quantum 01074 *restrict p; 01075 01076 register ssize_t 01077 x; 01078 01079 if (status == MagickFalse) 01080 continue; 01081 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 01082 if (p == (const Quantum *) NULL) 01083 { 01084 status=MagickFalse; 01085 continue; 01086 } 01087 for (x=0; x < (ssize_t) image->columns; x++) 01088 { 01089 register ssize_t 01090 i; 01091 01092 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01093 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++; 01094 p+=GetPixelChannels(image); 01095 } 01096 } 01097 /* 01098 Find the histogram boundaries by locating the black/white levels. 01099 */ 01100 number_channels=GetPixelChannels(image); 01101 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01102 #pragma omp parallel for schedule(static,4) shared(progress,status) 01103 #endif 01104 for (i=0; i < (ssize_t) number_channels; i++) 01105 { 01106 double 01107 intensity; 01108 01109 register ssize_t 01110 j; 01111 01112 black[i]=0.0; 01113 white[i]=MaxRange(QuantumRange); 01114 intensity=0.0; 01115 for (j=0; j <= (ssize_t) MaxMap; j++) 01116 { 01117 intensity+=histogram[GetPixelChannels(image)*j+i]; 01118 if (intensity > black_point) 01119 break; 01120 } 01121 black[i]=(MagickRealType) j; 01122 intensity=0.0; 01123 for (j=(ssize_t) MaxMap; j != 0; j--) 01124 { 01125 intensity+=histogram[GetPixelChannels(image)*j+i]; 01126 if (intensity > ((double) image->columns*image->rows-white_point)) 01127 break; 01128 } 01129 white[i]=(MagickRealType) j; 01130 } 01131 histogram=(double *) RelinquishMagickMemory(histogram); 01132 /* 01133 Stretch the histogram to create the stretched image mapping. 01134 */ 01135 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)* 01136 sizeof(*stretch_map)); 01137 number_channels=GetPixelChannels(image); 01138 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01139 #pragma omp parallel for schedule(static,4) shared(progress,status) 01140 #endif 01141 for (i=0; i < (ssize_t) number_channels; i++) 01142 { 01143 register ssize_t 01144 j; 01145 01146 for (j=0; j <= (ssize_t) MaxMap; j++) 01147 { 01148 if (j < (ssize_t) black[i]) 01149 stretch_map[GetPixelChannels(image)*j+i]=0.0; 01150 else 01151 if (j > (ssize_t) white[i]) 01152 stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType) 01153 QuantumRange; 01154 else 01155 if (black[i] != white[i]) 01156 stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType) 01157 ScaleMapToQuantum((MagickRealType) (MaxMap*(j-black[i])/ 01158 (white[i]-black[i]))); 01159 } 01160 } 01161 if (image->storage_class == PseudoClass) 01162 { 01163 register ssize_t 01164 j; 01165 01166 /* 01167 Stretch-contrast colormap. 01168 */ 01169 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01170 #pragma omp parallel for schedule(static,4) shared(progress,status) 01171 #endif 01172 for (j=0; j < (ssize_t) image->colors; j++) 01173 { 01174 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 01175 { 01176 i=GetPixelChannelMapChannel(image,RedPixelChannel); 01177 if (black[i] != white[i]) 01178 image->colormap[j].red=stretch_map[GetPixelChannels(image)* 01179 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+i; 01180 } 01181 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 01182 { 01183 i=GetPixelChannelMapChannel(image,GreenPixelChannel); 01184 if (black[i] != white[i]) 01185 image->colormap[j].green=stretch_map[GetPixelChannels(image)* 01186 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+i; 01187 } 01188 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 01189 { 01190 i=GetPixelChannelMapChannel(image,BluePixelChannel); 01191 if (black[i] != white[i]) 01192 image->colormap[j].blue=stretch_map[GetPixelChannels(image)* 01193 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+i; 01194 } 01195 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 01196 { 01197 i=GetPixelChannelMapChannel(image,AlphaPixelChannel); 01198 if (black[i] != white[i]) 01199 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)* 01200 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+i; 01201 } 01202 } 01203 } 01204 /* 01205 Stretch-contrast image. 01206 */ 01207 status=MagickTrue; 01208 progress=0; 01209 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01210 #pragma omp parallel for schedule(static,4) shared(progress,status) 01211 #endif 01212 for (y=0; y < (ssize_t) image->rows; y++) 01213 { 01214 register Quantum 01215 *restrict q; 01216 01217 register ssize_t 01218 x; 01219 01220 if (status == MagickFalse) 01221 continue; 01222 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01223 if (q == (Quantum *) NULL) 01224 { 01225 status=MagickFalse; 01226 continue; 01227 } 01228 for (x=0; x < (ssize_t) image->columns; x++) 01229 { 01230 register ssize_t 01231 i; 01232 01233 if (GetPixelMask(image,q) != 0) 01234 { 01235 q+=GetPixelChannels(image); 01236 continue; 01237 } 01238 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01239 { 01240 PixelChannel 01241 channel; 01242 01243 PixelTrait 01244 traits; 01245 01246 channel=GetPixelChannelMapChannel(image,i); 01247 traits=GetPixelChannelMapTraits(image,channel); 01248 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i])) 01249 continue; 01250 q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)* 01251 ScaleQuantumToMap(q[i])+i]); 01252 } 01253 q+=GetPixelChannels(image); 01254 } 01255 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01256 status=MagickFalse; 01257 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01258 { 01259 MagickBooleanType 01260 proceed; 01261 01262 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01263 #pragma omp critical (MagickCore_ContrastStretchImage) 01264 #endif 01265 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++, 01266 image->rows); 01267 if (proceed == MagickFalse) 01268 status=MagickFalse; 01269 } 01270 } 01271 image_view=DestroyCacheView(image_view); 01272 stretch_map=(double *) RelinquishMagickMemory(stretch_map); 01273 white=(double *) RelinquishMagickMemory(white); 01274 black=(double *) RelinquishMagickMemory(black); 01275 return(status); 01276 } 01277 01278 /* 01279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01280 % % 01281 % % 01282 % % 01283 % E n h a n c e I m a g e % 01284 % % 01285 % % 01286 % % 01287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01288 % 01289 % EnhanceImage() applies a digital filter that improves the quality of a 01290 % noisy image. 01291 % 01292 % The format of the EnhanceImage method is: 01293 % 01294 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception) 01295 % 01296 % A description of each parameter follows: 01297 % 01298 % o image: the image. 01299 % 01300 % o exception: return any errors or warnings in this structure. 01301 % 01302 */ 01303 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception) 01304 { 01305 #define EnhancePixel(weight) \ 01306 mean=((MagickRealType) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \ 01307 distance=(MagickRealType) r[i]-(MagickRealType) GetPixelChannel( \ 01308 enhance_image,channel,q); \ 01309 distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \ 01310 mean)*distance*distance; \ 01311 if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \ 01312 QuantumRange/25.0f)) \ 01313 { \ 01314 aggregate+=(weight)*r[i]; \ 01315 total_weight+=(weight); \ 01316 } \ 01317 r+=GetPixelChannels(image); 01318 #define EnhanceImageTag "Enhance/Image" 01319 01320 CacheView 01321 *enhance_view, 01322 *image_view; 01323 01324 Image 01325 *enhance_image; 01326 01327 MagickBooleanType 01328 status; 01329 01330 MagickOffsetType 01331 progress; 01332 01333 ssize_t 01334 y; 01335 01336 /* 01337 Initialize enhanced image attributes. 01338 */ 01339 assert(image != (const Image *) NULL); 01340 assert(image->signature == MagickSignature); 01341 if (image->debug != MagickFalse) 01342 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01343 assert(exception != (ExceptionInfo *) NULL); 01344 assert(exception->signature == MagickSignature); 01345 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue, 01346 exception); 01347 if (enhance_image == (Image *) NULL) 01348 return((Image *) NULL); 01349 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse) 01350 { 01351 enhance_image=DestroyImage(enhance_image); 01352 return((Image *) NULL); 01353 } 01354 /* 01355 Enhance image. 01356 */ 01357 status=MagickTrue; 01358 progress=0; 01359 image_view=AcquireCacheView(image); 01360 enhance_view=AcquireCacheView(enhance_image); 01361 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01362 #pragma omp parallel for schedule(static,4) shared(progress,status) 01363 #endif 01364 for (y=0; y < (ssize_t) image->rows; y++) 01365 { 01366 register const Quantum 01367 *restrict p; 01368 01369 register Quantum 01370 *restrict q; 01371 01372 register ssize_t 01373 x; 01374 01375 ssize_t 01376 center; 01377 01378 if (status == MagickFalse) 01379 continue; 01380 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception); 01381 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1, 01382 exception); 01383 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 01384 { 01385 status=MagickFalse; 01386 continue; 01387 } 01388 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2); 01389 for (x=0; x < (ssize_t) image->columns; x++) 01390 { 01391 register ssize_t 01392 i; 01393 01394 if (GetPixelMask(image,p) != 0) 01395 { 01396 p+=GetPixelChannels(image); 01397 q+=GetPixelChannels(enhance_image); 01398 continue; 01399 } 01400 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01401 { 01402 MagickRealType 01403 aggregate, 01404 distance, 01405 distance_squared, 01406 mean, 01407 total_weight; 01408 01409 PixelChannel 01410 channel; 01411 01412 PixelTrait 01413 enhance_traits, 01414 traits; 01415 01416 register const Quantum 01417 *restrict r; 01418 01419 channel=GetPixelChannelMapChannel(image,i); 01420 traits=GetPixelChannelMapTraits(image,channel); 01421 enhance_traits=GetPixelChannelMapTraits(enhance_image,channel); 01422 if ((traits == UndefinedPixelTrait) || 01423 (enhance_traits == UndefinedPixelTrait)) 01424 continue; 01425 SetPixelChannel(enhance_image,channel,p[center+i],q); 01426 if ((enhance_traits & CopyPixelTrait) != 0) 01427 continue; 01428 /* 01429 Compute weighted average of target pixel color components. 01430 */ 01431 aggregate=0.0; 01432 total_weight=0.0; 01433 r=p; 01434 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0); 01435 EnhancePixel(8.0); EnhancePixel(5.0); 01436 r=p+1*GetPixelChannels(image)*(image->columns+4); 01437 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0); 01438 EnhancePixel(20.0); EnhancePixel(8.0); 01439 r=p+2*GetPixelChannels(image)*(image->columns+4); 01440 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0); 01441 EnhancePixel(40.0); EnhancePixel(10.0); 01442 r=p+3*GetPixelChannels(image)*(image->columns+4); 01443 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0); 01444 EnhancePixel(20.0); EnhancePixel(8.0); 01445 r=p+4*GetPixelChannels(image)*(image->columns+4); 01446 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0); 01447 EnhancePixel(8.0); EnhancePixel(5.0); 01448 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/ 01449 total_weight),q); 01450 } 01451 p+=GetPixelChannels(image); 01452 q+=GetPixelChannels(enhance_image); 01453 } 01454 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse) 01455 status=MagickFalse; 01456 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01457 { 01458 MagickBooleanType 01459 proceed; 01460 01461 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01462 #pragma omp critical (MagickCore_EnhanceImage) 01463 #endif 01464 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows); 01465 if (proceed == MagickFalse) 01466 status=MagickFalse; 01467 } 01468 } 01469 enhance_view=DestroyCacheView(enhance_view); 01470 image_view=DestroyCacheView(image_view); 01471 return(enhance_image); 01472 } 01473 01474 /* 01475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01476 % % 01477 % % 01478 % % 01479 % E q u a l i z e I m a g e % 01480 % % 01481 % % 01482 % % 01483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01484 % 01485 % EqualizeImage() applies a histogram equalization to the image. 01486 % 01487 % The format of the EqualizeImage method is: 01488 % 01489 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception) 01490 % 01491 % A description of each parameter follows: 01492 % 01493 % o image: the image. 01494 % 01495 % o exception: return any errors or warnings in this structure. 01496 % 01497 */ 01498 MagickExport MagickBooleanType EqualizeImage(Image *image, 01499 ExceptionInfo *exception) 01500 { 01501 #define EqualizeImageTag "Equalize/Image" 01502 01503 CacheView 01504 *image_view; 01505 01506 MagickBooleanType 01507 status; 01508 01509 MagickOffsetType 01510 progress; 01511 01512 MagickRealType 01513 black[CompositePixelChannel], 01514 *equalize_map, 01515 *histogram, 01516 *map, 01517 white[CompositePixelChannel]; 01518 01519 register ssize_t 01520 i; 01521 01522 size_t 01523 number_channels; 01524 01525 ssize_t 01526 y; 01527 01528 /* 01529 Allocate and initialize histogram arrays. 01530 */ 01531 assert(image != (Image *) NULL); 01532 assert(image->signature == MagickSignature); 01533 if (image->debug != MagickFalse) 01534 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01535 equalize_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL, 01536 GetPixelChannels(image)*sizeof(*equalize_map)); 01537 histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL, 01538 GetPixelChannels(image)*sizeof(*histogram)); 01539 map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL, 01540 GetPixelChannels(image)*sizeof(*map)); 01541 if ((equalize_map == (MagickRealType *) NULL) || 01542 (histogram == (MagickRealType *) NULL) || 01543 (map == (MagickRealType *) NULL)) 01544 { 01545 if (map != (MagickRealType *) NULL) 01546 map=(MagickRealType *) RelinquishMagickMemory(map); 01547 if (histogram != (MagickRealType *) NULL) 01548 histogram=(MagickRealType *) RelinquishMagickMemory(histogram); 01549 if (equalize_map != (MagickRealType *) NULL) 01550 equalize_map=(MagickRealType *) RelinquishMagickMemory(equalize_map); 01551 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01552 image->filename); 01553 } 01554 /* 01555 Form histogram. 01556 */ 01557 status=MagickTrue; 01558 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)* 01559 sizeof(*histogram)); 01560 image_view=AcquireCacheView(image); 01561 for (y=0; y < (ssize_t) image->rows; y++) 01562 { 01563 register const Quantum 01564 *restrict p; 01565 01566 register ssize_t 01567 x; 01568 01569 if (status == MagickFalse) 01570 continue; 01571 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 01572 if (p == (const Quantum *) NULL) 01573 { 01574 status=MagickFalse; 01575 continue; 01576 } 01577 for (x=0; x < (ssize_t) image->columns; x++) 01578 { 01579 register ssize_t 01580 i; 01581 01582 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01583 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++; 01584 p+=GetPixelChannels(image); 01585 } 01586 } 01587 /* 01588 Integrate the histogram to get the equalization map. 01589 */ 01590 number_channels=GetPixelChannels(image); 01591 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01592 #pragma omp parallel for schedule(static,4) shared(progress,status) 01593 #endif 01594 for (i=0; i < (ssize_t) number_channels; i++) 01595 { 01596 MagickRealType 01597 intensity; 01598 01599 register ssize_t 01600 j; 01601 01602 intensity=0.0; 01603 for (j=0; j <= (ssize_t) MaxMap; j++) 01604 { 01605 intensity+=histogram[GetPixelChannels(image)*j+i]; 01606 map[GetPixelChannels(image)*j+i]=intensity; 01607 } 01608 } 01609 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)* 01610 sizeof(*equalize_map)); 01611 number_channels=GetPixelChannels(image); 01612 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01613 #pragma omp parallel for schedule(static,4) shared(progress,status) 01614 #endif 01615 for (i=0; i < (ssize_t) number_channels; i++) 01616 { 01617 register ssize_t 01618 j; 01619 01620 black[i]=map[i]; 01621 white[i]=map[GetPixelChannels(image)*MaxMap+i]; 01622 if (black[i] != white[i]) 01623 for (j=0; j <= (ssize_t) MaxMap; j++) 01624 equalize_map[GetPixelChannels(image)*j+i]=(MagickRealType) 01625 ScaleMapToQuantum((MagickRealType) ((MaxMap*(map[ 01626 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i]))); 01627 } 01628 histogram=(MagickRealType *) RelinquishMagickMemory(histogram); 01629 map=(MagickRealType *) RelinquishMagickMemory(map); 01630 if (image->storage_class == PseudoClass) 01631 { 01632 PixelChannel 01633 channel; 01634 01635 register ssize_t 01636 j; 01637 01638 /* 01639 Equalize colormap. 01640 */ 01641 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01642 #pragma omp parallel for schedule(static,4) shared(progress,status) 01643 #endif 01644 for (j=0; j < (ssize_t) image->colors; j++) 01645 { 01646 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 01647 { 01648 channel=GetPixelChannelMapChannel(image,RedPixelChannel); 01649 if (black[channel] != white[channel]) 01650 image->colormap[j].red=equalize_map[GetPixelChannels(image)* 01651 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+ 01652 channel; 01653 } 01654 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 01655 { 01656 channel=GetPixelChannelMapChannel(image,GreenPixelChannel); 01657 if (black[channel] != white[channel]) 01658 image->colormap[j].green=equalize_map[GetPixelChannels(image)* 01659 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+ 01660 channel; 01661 } 01662 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 01663 { 01664 channel=GetPixelChannelMapChannel(image,BluePixelChannel); 01665 if (black[channel] != white[channel]) 01666 image->colormap[j].blue=equalize_map[GetPixelChannels(image)* 01667 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+ 01668 channel; 01669 } 01670 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 01671 { 01672 channel=GetPixelChannelMapChannel(image,AlphaPixelChannel); 01673 if (black[channel] != white[channel]) 01674 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)* 01675 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+ 01676 channel; 01677 } 01678 } 01679 } 01680 /* 01681 Equalize image. 01682 */ 01683 progress=0; 01684 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01685 #pragma omp parallel for schedule(static,4) shared(progress,status) 01686 #endif 01687 for (y=0; y < (ssize_t) image->rows; y++) 01688 { 01689 register Quantum 01690 *restrict q; 01691 01692 register ssize_t 01693 x; 01694 01695 if (status == MagickFalse) 01696 continue; 01697 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01698 if (q == (Quantum *) NULL) 01699 { 01700 status=MagickFalse; 01701 continue; 01702 } 01703 for (x=0; x < (ssize_t) image->columns; x++) 01704 { 01705 register ssize_t 01706 i; 01707 01708 if (GetPixelMask(image,q) != 0) 01709 { 01710 q+=GetPixelChannels(image); 01711 continue; 01712 } 01713 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01714 { 01715 PixelChannel 01716 channel; 01717 01718 PixelTrait 01719 traits; 01720 01721 channel=GetPixelChannelMapChannel(image,i); 01722 traits=GetPixelChannelMapTraits(image,channel); 01723 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i])) 01724 continue; 01725 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)* 01726 ScaleQuantumToMap(q[i])+i]); 01727 } 01728 q+=GetPixelChannels(image); 01729 } 01730 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01731 status=MagickFalse; 01732 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01733 { 01734 MagickBooleanType 01735 proceed; 01736 01737 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01738 #pragma omp critical (MagickCore_EqualizeImage) 01739 #endif 01740 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows); 01741 if (proceed == MagickFalse) 01742 status=MagickFalse; 01743 } 01744 } 01745 image_view=DestroyCacheView(image_view); 01746 equalize_map=(MagickRealType *) RelinquishMagickMemory(equalize_map); 01747 return(status); 01748 } 01749 01750 /* 01751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01752 % % 01753 % % 01754 % % 01755 % G a m m a I m a g e % 01756 % % 01757 % % 01758 % % 01759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01760 % 01761 % GammaImage() gamma-corrects a particular image channel. The same 01762 % image viewed on different devices will have perceptual differences in the 01763 % way the image's intensities are represented on the screen. Specify 01764 % individual gamma levels for the red, green, and blue channels, or adjust 01765 % all three with the gamma parameter. Values typically range from 0.8 to 2.3. 01766 % 01767 % You can also reduce the influence of a particular channel with a gamma 01768 % value of 0. 01769 % 01770 % The format of the GammaImage method is: 01771 % 01772 % MagickBooleanType GammaImage(Image *image,const double gamma, 01773 % ExceptionInfo *exception) 01774 % 01775 % A description of each parameter follows: 01776 % 01777 % o image: the image. 01778 % 01779 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0). 01780 % 01781 % o gamma: the image gamma. 01782 % 01783 */ 01784 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma, 01785 ExceptionInfo *exception) 01786 { 01787 #define GammaCorrectImageTag "GammaCorrect/Image" 01788 01789 CacheView 01790 *image_view; 01791 01792 MagickBooleanType 01793 status; 01794 01795 MagickOffsetType 01796 progress; 01797 01798 Quantum 01799 *gamma_map; 01800 01801 register ssize_t 01802 i; 01803 01804 ssize_t 01805 y; 01806 01807 /* 01808 Allocate and initialize gamma maps. 01809 */ 01810 assert(image != (Image *) NULL); 01811 assert(image->signature == MagickSignature); 01812 if (image->debug != MagickFalse) 01813 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01814 if (gamma == 1.0) 01815 return(MagickTrue); 01816 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map)); 01817 if (gamma_map == (Quantum *) NULL) 01818 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01819 image->filename); 01820 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map)); 01821 if (gamma != 0.0) 01822 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (MaxMap > 256) 01823 #pragma omp parallel for 01824 #endif 01825 for (i=0; i <= (ssize_t) MaxMap; i++) 01826 gamma_map[i]=ScaleMapToQuantum((MagickRealType) (MaxMap*pow((double) i/ 01827 MaxMap,1.0/gamma))); 01828 if (image->storage_class == PseudoClass) 01829 { 01830 /* 01831 Gamma-correct colormap. 01832 */ 01833 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01834 #pragma omp parallel for schedule(static) shared(progress,status) 01835 #endif 01836 for (i=0; i < (ssize_t) image->colors; i++) 01837 { 01838 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 01839 image->colormap[i].red=(MagickRealType) gamma_map[ 01840 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))]; 01841 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 01842 image->colormap[i].green=(MagickRealType) gamma_map[ 01843 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))]; 01844 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 01845 image->colormap[i].blue=(MagickRealType) gamma_map[ 01846 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))]; 01847 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 01848 image->colormap[i].alpha=(MagickRealType) gamma_map[ 01849 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].alpha))]; 01850 } 01851 } 01852 /* 01853 Gamma-correct image. 01854 */ 01855 status=MagickTrue; 01856 progress=0; 01857 image_view=AcquireCacheView(image); 01858 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01859 #pragma omp parallel for schedule(static,4) shared(progress,status) 01860 #endif 01861 for (y=0; y < (ssize_t) image->rows; y++) 01862 { 01863 register Quantum 01864 *restrict q; 01865 01866 register ssize_t 01867 x; 01868 01869 if (status == MagickFalse) 01870 continue; 01871 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01872 if (q == (Quantum *) NULL) 01873 { 01874 status=MagickFalse; 01875 continue; 01876 } 01877 for (x=0; x < (ssize_t) image->columns; x++) 01878 { 01879 register ssize_t 01880 i; 01881 01882 if (GetPixelMask(image,q) != 0) 01883 { 01884 q+=GetPixelChannels(image); 01885 continue; 01886 } 01887 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01888 { 01889 PixelChannel 01890 channel; 01891 01892 PixelTrait 01893 traits; 01894 01895 channel=GetPixelChannelMapChannel(image,i); 01896 traits=GetPixelChannelMapTraits(image,channel); 01897 if ((traits & UpdatePixelTrait) == 0) 01898 continue; 01899 q[i]=gamma_map[ScaleQuantumToMap(q[i])]; 01900 } 01901 q+=GetPixelChannels(image); 01902 } 01903 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01904 status=MagickFalse; 01905 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01906 { 01907 MagickBooleanType 01908 proceed; 01909 01910 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01911 #pragma omp critical (MagickCore_GammaImage) 01912 #endif 01913 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++, 01914 image->rows); 01915 if (proceed == MagickFalse) 01916 status=MagickFalse; 01917 } 01918 } 01919 image_view=DestroyCacheView(image_view); 01920 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map); 01921 if (image->gamma != 0.0) 01922 image->gamma*=gamma; 01923 return(status); 01924 } 01925 01926 /* 01927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01928 % % 01929 % % 01930 % % 01931 % H a l d C l u t I m a g e % 01932 % % 01933 % % 01934 % % 01935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01936 % 01937 % HaldClutImage() applies a Hald color lookup table to the image. A Hald 01938 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions. 01939 % Create it with the HALD coder. You can apply any color transformation to 01940 % the Hald image and then use this method to apply the transform to the 01941 % image. 01942 % 01943 % The format of the HaldClutImage method is: 01944 % 01945 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image, 01946 % ExceptionInfo *exception) 01947 % 01948 % A description of each parameter follows: 01949 % 01950 % o image: the image, which is replaced by indexed CLUT values 01951 % 01952 % o hald_image: the color lookup table image for replacement color values. 01953 % 01954 % o exception: return any errors or warnings in this structure. 01955 % 01956 */ 01957 01958 static inline size_t MagickMin(const size_t x,const size_t y) 01959 { 01960 if (x < y) 01961 return(x); 01962 return(y); 01963 } 01964 01965 MagickExport MagickBooleanType HaldClutImage(Image *image, 01966 const Image *hald_image,ExceptionInfo *exception) 01967 { 01968 #define HaldClutImageTag "Clut/Image" 01969 01970 typedef struct _HaldInfo 01971 { 01972 MagickRealType 01973 x, 01974 y, 01975 z; 01976 } HaldInfo; 01977 01978 CacheView 01979 *hald_view, 01980 *image_view; 01981 01982 double 01983 width; 01984 01985 MagickBooleanType 01986 status; 01987 01988 MagickOffsetType 01989 progress; 01990 01991 PixelInfo 01992 zero; 01993 01994 size_t 01995 cube_size, 01996 length, 01997 level; 01998 01999 ssize_t 02000 y; 02001 02002 assert(image != (Image *) NULL); 02003 assert(image->signature == MagickSignature); 02004 if (image->debug != MagickFalse) 02005 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02006 assert(hald_image != (Image *) NULL); 02007 assert(hald_image->signature == MagickSignature); 02008 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 02009 return(MagickFalse); 02010 if (image->matte == MagickFalse) 02011 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 02012 /* 02013 Hald clut image. 02014 */ 02015 status=MagickTrue; 02016 progress=0; 02017 length=MagickMin(hald_image->columns,hald_image->rows); 02018 for (level=2; (level*level*level) < length; level++) ; 02019 level*=level; 02020 cube_size=level*level; 02021 width=(double) hald_image->columns; 02022 GetPixelInfo(hald_image,&zero); 02023 image_view=AcquireCacheView(image); 02024 hald_view=AcquireCacheView(hald_image); 02025 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02026 #pragma omp parallel for schedule(static,4) shared(progress,status) 02027 #endif 02028 for (y=0; y < (ssize_t) image->rows; y++) 02029 { 02030 register Quantum 02031 *restrict q; 02032 02033 register ssize_t 02034 x; 02035 02036 if (status == MagickFalse) 02037 continue; 02038 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02039 if (q == (Quantum *) NULL) 02040 { 02041 status=MagickFalse; 02042 continue; 02043 } 02044 for (x=0; x < (ssize_t) image->columns; x++) 02045 { 02046 double 02047 offset; 02048 02049 HaldInfo 02050 point; 02051 02052 PixelInfo 02053 pixel, 02054 pixel1, 02055 pixel2, 02056 pixel3, 02057 pixel4; 02058 02059 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q); 02060 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q); 02061 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q); 02062 offset=point.x+level*floor(point.y)+cube_size*floor(point.z); 02063 point.x-=floor(point.x); 02064 point.y-=floor(point.y); 02065 point.z-=floor(point.z); 02066 pixel1=zero; 02067 (void) InterpolatePixelInfo(image,hald_view,image->interpolate, 02068 fmod(offset,width),floor(offset/width),&pixel1,exception); 02069 pixel2=zero; 02070 (void) InterpolatePixelInfo(image,hald_view,image->interpolate, 02071 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception); 02072 pixel3=zero; 02073 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha, 02074 point.y,&pixel3); 02075 offset+=cube_size; 02076 (void) InterpolatePixelInfo(image,hald_view,image->interpolate, 02077 fmod(offset,width),floor(offset/width),&pixel1,exception); 02078 (void) InterpolatePixelInfo(image,hald_view,image->interpolate, 02079 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception); 02080 pixel4=zero; 02081 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha, 02082 point.y,&pixel4); 02083 pixel=zero; 02084 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha, 02085 point.z,&pixel); 02086 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02087 SetPixelRed(image,ClampToQuantum(pixel.red),q); 02088 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02089 SetPixelGreen(image,ClampToQuantum(pixel.green),q); 02090 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02091 SetPixelBlue(image,ClampToQuantum(pixel.blue),q); 02092 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 02093 (image->colorspace == CMYKColorspace)) 02094 SetPixelBlack(image,ClampToQuantum(pixel.black),q); 02095 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 02096 (image->matte != MagickFalse)) 02097 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); 02098 q+=GetPixelChannels(image); 02099 } 02100 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02101 status=MagickFalse; 02102 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02103 { 02104 MagickBooleanType 02105 proceed; 02106 02107 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02108 #pragma omp critical (MagickCore_HaldClutImage) 02109 #endif 02110 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows); 02111 if (proceed == MagickFalse) 02112 status=MagickFalse; 02113 } 02114 } 02115 hald_view=DestroyCacheView(hald_view); 02116 image_view=DestroyCacheView(image_view); 02117 return(status); 02118 } 02119 02120 /* 02121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02122 % % 02123 % % 02124 % % 02125 % L e v e l I m a g e % 02126 % % 02127 % % 02128 % % 02129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02130 % 02131 % LevelImage() adjusts the levels of a particular image channel by 02132 % scaling the colors falling between specified white and black points to 02133 % the full available quantum range. 02134 % 02135 % The parameters provided represent the black, and white points. The black 02136 % point specifies the darkest color in the image. Colors darker than the 02137 % black point are set to zero. White point specifies the lightest color in 02138 % the image. Colors brighter than the white point are set to the maximum 02139 % quantum value. 02140 % 02141 % If a '!' flag is given, map black and white colors to the given levels 02142 % rather than mapping those levels to black and white. See 02143 % LevelizeImage() below. 02144 % 02145 % Gamma specifies a gamma correction to apply to the image. 02146 % 02147 % The format of the LevelImage method is: 02148 % 02149 % MagickBooleanType LevelImage(Image *image,const double black_point, 02150 % const double white_point,const double gamma,ExceptionInfo *exception) 02151 % 02152 % A description of each parameter follows: 02153 % 02154 % o image: the image. 02155 % 02156 % o black_point: The level to map zero (black) to. 02157 % 02158 % o white_point: The level to map QuantumRange (white) to. 02159 % 02160 % o exception: return any errors or warnings in this structure. 02161 % 02162 */ 02163 02164 static inline MagickRealType LevelPixel(const double black_point, 02165 const double white_point,const double gamma,const MagickRealType pixel) 02166 { 02167 double 02168 level_pixel, 02169 scale; 02170 02171 if (pixel < black_point) 02172 return(0.0); 02173 if (pixel > white_point) 02174 return((MagickRealType) QuantumRange); 02175 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0; 02176 level_pixel=(MagickRealType) QuantumRange*pow(scale*((double) pixel- 02177 black_point),1.0/gamma); 02178 return(level_pixel); 02179 } 02180 02181 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point, 02182 const double white_point,const double gamma,ExceptionInfo *exception) 02183 { 02184 #define LevelImageTag "Level/Image" 02185 02186 CacheView 02187 *image_view; 02188 02189 MagickBooleanType 02190 status; 02191 02192 MagickOffsetType 02193 progress; 02194 02195 register ssize_t 02196 i; 02197 02198 ssize_t 02199 y; 02200 02201 /* 02202 Allocate and initialize levels map. 02203 */ 02204 assert(image != (Image *) NULL); 02205 assert(image->signature == MagickSignature); 02206 if (image->debug != MagickFalse) 02207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02208 if (image->storage_class == PseudoClass) 02209 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02210 #pragma omp parallel for schedule(static,4) shared(progress,status) 02211 #endif 02212 for (i=0; i < (ssize_t) image->colors; i++) 02213 { 02214 /* 02215 Level colormap. 02216 */ 02217 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02218 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point, 02219 white_point,gamma,image->colormap[i].red)); 02220 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02221 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point, 02222 white_point,gamma,image->colormap[i].green)); 02223 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02224 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point, 02225 white_point,gamma,image->colormap[i].blue)); 02226 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 02227 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point, 02228 white_point,gamma,image->colormap[i].alpha)); 02229 } 02230 /* 02231 Level image. 02232 */ 02233 status=MagickTrue; 02234 progress=0; 02235 image_view=AcquireCacheView(image); 02236 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02237 #pragma omp parallel for schedule(static,4) shared(progress,status) 02238 #endif 02239 for (y=0; y < (ssize_t) image->rows; y++) 02240 { 02241 register Quantum 02242 *restrict q; 02243 02244 register ssize_t 02245 x; 02246 02247 if (status == MagickFalse) 02248 continue; 02249 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02250 if (q == (Quantum *) NULL) 02251 { 02252 status=MagickFalse; 02253 continue; 02254 } 02255 for (x=0; x < (ssize_t) image->columns; x++) 02256 { 02257 register ssize_t 02258 i; 02259 02260 if (GetPixelMask(image,q) != 0) 02261 { 02262 q+=GetPixelChannels(image); 02263 continue; 02264 } 02265 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 02266 { 02267 PixelChannel 02268 channel; 02269 02270 PixelTrait 02271 traits; 02272 02273 channel=GetPixelChannelMapChannel(image,i); 02274 traits=GetPixelChannelMapTraits(image,channel); 02275 if ((traits & UpdatePixelTrait) == 0) 02276 continue; 02277 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma, 02278 (MagickRealType) q[i])); 02279 } 02280 q+=GetPixelChannels(image); 02281 } 02282 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02283 status=MagickFalse; 02284 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02285 { 02286 MagickBooleanType 02287 proceed; 02288 02289 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02290 #pragma omp critical (MagickCore_LevelImage) 02291 #endif 02292 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows); 02293 if (proceed == MagickFalse) 02294 status=MagickFalse; 02295 } 02296 } 02297 image_view=DestroyCacheView(image_view); 02298 return(status); 02299 } 02300 02301 /* 02302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02303 % % 02304 % % 02305 % % 02306 % L e v e l i z e I m a g e % 02307 % % 02308 % % 02309 % % 02310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02311 % 02312 % LevelizeImage() applies the reversed LevelImage() operation to just 02313 % the specific channels specified. It compresses the full range of color 02314 % values, so that they lie between the given black and white points. Gamma is 02315 % applied before the values are mapped. 02316 % 02317 % LevelizeImage() can be called with by using a +level command line 02318 % API option, or using a '!' on a -level or LevelImage() geometry string. 02319 % 02320 % It can be used to de-contrast a greyscale image to the exact levels 02321 % specified. Or by using specific levels for each channel of an image you 02322 % can convert a gray-scale image to any linear color gradient, according to 02323 % those levels. 02324 % 02325 % The format of the LevelizeImage method is: 02326 % 02327 % MagickBooleanType LevelizeImage(Image *image,const double black_point, 02328 % const double white_point,const double gamma,ExceptionInfo *exception) 02329 % 02330 % A description of each parameter follows: 02331 % 02332 % o image: the image. 02333 % 02334 % o black_point: The level to map zero (black) to. 02335 % 02336 % o white_point: The level to map QuantumRange (white) to. 02337 % 02338 % o gamma: adjust gamma by this factor before mapping values. 02339 % 02340 % o exception: return any errors or warnings in this structure. 02341 % 02342 */ 02343 MagickExport MagickBooleanType LevelizeImage(Image *image, 02344 const double black_point,const double white_point,const double gamma, 02345 ExceptionInfo *exception) 02346 { 02347 #define LevelizeImageTag "Levelize/Image" 02348 #define LevelizeValue(x) (ClampToQuantum(((MagickRealType) \ 02349 pow((double) (QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \ 02350 black_point)) 02351 02352 CacheView 02353 *image_view; 02354 02355 MagickBooleanType 02356 status; 02357 02358 MagickOffsetType 02359 progress; 02360 02361 register ssize_t 02362 i; 02363 02364 ssize_t 02365 y; 02366 02367 /* 02368 Allocate and initialize levels map. 02369 */ 02370 assert(image != (Image *) NULL); 02371 assert(image->signature == MagickSignature); 02372 if (image->debug != MagickFalse) 02373 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02374 if (image->storage_class == PseudoClass) 02375 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02376 #pragma omp parallel for schedule(static,4) shared(progress,status) 02377 #endif 02378 for (i=0; i < (ssize_t) image->colors; i++) 02379 { 02380 /* 02381 Level colormap. 02382 */ 02383 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02384 image->colormap[i].red=(double) LevelizeValue( 02385 image->colormap[i].red); 02386 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02387 image->colormap[i].green=(double) LevelizeValue( 02388 image->colormap[i].green); 02389 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02390 image->colormap[i].blue=(double) LevelizeValue( 02391 image->colormap[i].blue); 02392 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 02393 image->colormap[i].alpha=(double) LevelizeValue( 02394 image->colormap[i].alpha); 02395 } 02396 /* 02397 Level image. 02398 */ 02399 status=MagickTrue; 02400 progress=0; 02401 image_view=AcquireCacheView(image); 02402 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02403 #pragma omp parallel for schedule(static,4) shared(progress,status) 02404 #endif 02405 for (y=0; y < (ssize_t) image->rows; y++) 02406 { 02407 register Quantum 02408 *restrict q; 02409 02410 register ssize_t 02411 x; 02412 02413 if (status == MagickFalse) 02414 continue; 02415 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02416 if (q == (Quantum *) NULL) 02417 { 02418 status=MagickFalse; 02419 continue; 02420 } 02421 for (x=0; x < (ssize_t) image->columns; x++) 02422 { 02423 register ssize_t 02424 i; 02425 02426 if (GetPixelMask(image,q) != 0) 02427 { 02428 q+=GetPixelChannels(image); 02429 continue; 02430 } 02431 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 02432 { 02433 PixelChannel 02434 channel; 02435 02436 PixelTrait 02437 traits; 02438 02439 channel=GetPixelChannelMapChannel(image,i); 02440 traits=GetPixelChannelMapTraits(image,channel); 02441 if ((traits & UpdatePixelTrait) == 0) 02442 continue; 02443 q[i]=LevelizeValue(q[i]); 02444 } 02445 q+=GetPixelChannels(image); 02446 } 02447 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02448 status=MagickFalse; 02449 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02450 { 02451 MagickBooleanType 02452 proceed; 02453 02454 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02455 #pragma omp critical (MagickCore_LevelizeImage) 02456 #endif 02457 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows); 02458 if (proceed == MagickFalse) 02459 status=MagickFalse; 02460 } 02461 } 02462 image_view=DestroyCacheView(image_view); 02463 return(status); 02464 } 02465 02466 /* 02467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02468 % % 02469 % % 02470 % % 02471 % L e v e l I m a g e C o l o r s % 02472 % % 02473 % % 02474 % % 02475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02476 % 02477 % LevelImageColors() maps the given color to "black" and "white" values, 02478 % linearly spreading out the colors, and level values on a channel by channel 02479 % bases, as per LevelImage(). The given colors allows you to specify 02480 % different level ranges for each of the color channels separately. 02481 % 02482 % If the boolean 'invert' is set true the image values will modifyed in the 02483 % reverse direction. That is any existing "black" and "white" colors in the 02484 % image will become the color values given, with all other values compressed 02485 % appropriatally. This effectivally maps a greyscale gradient into the given 02486 % color gradient. 02487 % 02488 % The format of the LevelImageColors method is: 02489 % 02490 % MagickBooleanType LevelImageColors(Image *image, 02491 % const PixelInfo *black_color,const PixelInfo *white_color, 02492 % const MagickBooleanType invert,ExceptionInfo *exception) 02493 % 02494 % A description of each parameter follows: 02495 % 02496 % o image: the image. 02497 % 02498 % o black_color: The color to map black to/from 02499 % 02500 % o white_point: The color to map white to/from 02501 % 02502 % o invert: if true map the colors (levelize), rather than from (level) 02503 % 02504 % o exception: return any errors or warnings in this structure. 02505 % 02506 */ 02507 MagickExport MagickBooleanType LevelImageColors(Image *image, 02508 const PixelInfo *black_color,const PixelInfo *white_color, 02509 const MagickBooleanType invert,ExceptionInfo *exception) 02510 { 02511 ChannelType 02512 channel_mask; 02513 02514 MagickStatusType 02515 status; 02516 02517 /* 02518 Allocate and initialize levels map. 02519 */ 02520 assert(image != (Image *) NULL); 02521 assert(image->signature == MagickSignature); 02522 if (image->debug != MagickFalse) 02523 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02524 status=MagickFalse; 02525 if (invert == MagickFalse) 02526 { 02527 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02528 { 02529 channel_mask=SetPixelChannelMask(image,RedChannel); 02530 status|=LevelImage(image,black_color->red,white_color->red,1.0, 02531 exception); 02532 (void) SetPixelChannelMask(image,channel_mask); 02533 } 02534 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02535 { 02536 channel_mask=SetPixelChannelMask(image,GreenChannel); 02537 status|=LevelImage(image,black_color->green,white_color->green,1.0, 02538 exception); 02539 (void) SetPixelChannelMask(image,channel_mask); 02540 } 02541 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02542 { 02543 channel_mask=SetPixelChannelMask(image,BlueChannel); 02544 status|=LevelImage(image,black_color->blue,white_color->blue,1.0, 02545 exception); 02546 (void) SetPixelChannelMask(image,channel_mask); 02547 } 02548 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 02549 (image->colorspace == CMYKColorspace)) 02550 { 02551 channel_mask=SetPixelChannelMask(image,BlackChannel); 02552 status|=LevelImage(image,black_color->black,white_color->black,1.0, 02553 exception); 02554 (void) SetPixelChannelMask(image,channel_mask); 02555 } 02556 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 02557 (image->matte == MagickTrue)) 02558 { 02559 channel_mask=SetPixelChannelMask(image,AlphaChannel); 02560 status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0, 02561 exception); 02562 (void) SetPixelChannelMask(image,channel_mask); 02563 } 02564 } 02565 else 02566 { 02567 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02568 { 02569 channel_mask=SetPixelChannelMask(image,RedChannel); 02570 status|=LevelizeImage(image,black_color->red,white_color->red,1.0, 02571 exception); 02572 (void) SetPixelChannelMask(image,channel_mask); 02573 } 02574 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02575 { 02576 channel_mask=SetPixelChannelMask(image,GreenChannel); 02577 status|=LevelizeImage(image,black_color->green,white_color->green,1.0, 02578 exception); 02579 (void) SetPixelChannelMask(image,channel_mask); 02580 } 02581 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02582 { 02583 channel_mask=SetPixelChannelMask(image,BlueChannel); 02584 status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0, 02585 exception); 02586 (void) SetPixelChannelMask(image,channel_mask); 02587 } 02588 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 02589 (image->colorspace == CMYKColorspace)) 02590 { 02591 channel_mask=SetPixelChannelMask(image,BlackChannel); 02592 status|=LevelizeImage(image,black_color->black,white_color->black,1.0, 02593 exception); 02594 (void) SetPixelChannelMask(image,channel_mask); 02595 } 02596 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 02597 (image->matte == MagickTrue)) 02598 { 02599 channel_mask=SetPixelChannelMask(image,AlphaChannel); 02600 status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0, 02601 exception); 02602 (void) SetPixelChannelMask(image,channel_mask); 02603 } 02604 } 02605 return(status == 0 ? MagickFalse : MagickTrue); 02606 } 02607 02608 /* 02609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02610 % % 02611 % % 02612 % % 02613 % L i n e a r S t r e t c h I m a g e % 02614 % % 02615 % % 02616 % % 02617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02618 % 02619 % LinearStretchImage() discards any pixels below the black point and above 02620 % the white point and levels the remaining pixels. 02621 % 02622 % The format of the LinearStretchImage method is: 02623 % 02624 % MagickBooleanType LinearStretchImage(Image *image, 02625 % const double black_point,const double white_point, 02626 % ExceptionInfo *exception) 02627 % 02628 % A description of each parameter follows: 02629 % 02630 % o image: the image. 02631 % 02632 % o black_point: the black point. 02633 % 02634 % o white_point: the white point. 02635 % 02636 % o exception: return any errors or warnings in this structure. 02637 % 02638 */ 02639 MagickExport MagickBooleanType LinearStretchImage(Image *image, 02640 const double black_point,const double white_point,ExceptionInfo *exception) 02641 { 02642 #define LinearStretchImageTag "LinearStretch/Image" 02643 02644 CacheView 02645 *image_view; 02646 02647 MagickBooleanType 02648 status; 02649 02650 MagickRealType 02651 *histogram, 02652 intensity; 02653 02654 ssize_t 02655 black, 02656 white, 02657 y; 02658 02659 /* 02660 Allocate histogram and linear map. 02661 */ 02662 assert(image != (Image *) NULL); 02663 assert(image->signature == MagickSignature); 02664 histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL, 02665 sizeof(*histogram)); 02666 if (histogram == (MagickRealType *) NULL) 02667 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 02668 image->filename); 02669 /* 02670 Form histogram. 02671 */ 02672 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram)); 02673 image_view=AcquireCacheView(image); 02674 for (y=0; y < (ssize_t) image->rows; y++) 02675 { 02676 register const Quantum 02677 *restrict p; 02678 02679 register ssize_t 02680 x; 02681 02682 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 02683 if (p == (const Quantum *) NULL) 02684 break; 02685 for (x=0; x < (ssize_t) image->columns; x++) 02686 { 02687 histogram[ScaleQuantumToMap(GetPixelIntensity(image,p))]++; 02688 p+=GetPixelChannels(image); 02689 } 02690 } 02691 image_view=DestroyCacheView(image_view); 02692 /* 02693 Find the histogram boundaries by locating the black and white point levels. 02694 */ 02695 intensity=0.0; 02696 for (black=0; black < (ssize_t) MaxMap; black++) 02697 { 02698 intensity+=histogram[black]; 02699 if (intensity >= black_point) 02700 break; 02701 } 02702 intensity=0.0; 02703 for (white=(ssize_t) MaxMap; white != 0; white--) 02704 { 02705 intensity+=histogram[white]; 02706 if (intensity >= white_point) 02707 break; 02708 } 02709 histogram=(MagickRealType *) RelinquishMagickMemory(histogram); 02710 status=LevelImage(image,(double) black,(double) white,1.0,exception); 02711 return(status); 02712 } 02713 02714 /* 02715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02716 % % 02717 % % 02718 % % 02719 % M o d u l a t e I m a g e % 02720 % % 02721 % % 02722 % % 02723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02724 % 02725 % ModulateImage() lets you control the brightness, saturation, and hue 02726 % of an image. Modulate represents the brightness, saturation, and hue 02727 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the 02728 % modulation is lightness, saturation, and hue. And if the colorspace is 02729 % HWB, use blackness, whiteness, and hue. 02730 % 02731 % The format of the ModulateImage method is: 02732 % 02733 % MagickBooleanType ModulateImage(Image *image,const char *modulate, 02734 % ExceptionInfo *exception) 02735 % 02736 % A description of each parameter follows: 02737 % 02738 % o image: the image. 02739 % 02740 % o modulate: Define the percent change in brightness, saturation, and hue. 02741 % 02742 % o exception: return any errors or warnings in this structure. 02743 % 02744 */ 02745 02746 static void ModulateHSB(const double percent_hue, 02747 const double percent_saturation,const double percent_brightness,double *red, 02748 double *green,double *blue) 02749 { 02750 double 02751 brightness, 02752 hue, 02753 saturation; 02754 02755 /* 02756 Increase or decrease color brightness, saturation, or hue. 02757 */ 02758 assert(red != (double *) NULL); 02759 assert(green != (double *) NULL); 02760 assert(blue != (double *) NULL); 02761 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness); 02762 hue+=0.5*(0.01*percent_hue-1.0); 02763 while (hue < 0.0) 02764 hue+=1.0; 02765 while (hue > 1.0) 02766 hue-=1.0; 02767 saturation*=0.01*percent_saturation; 02768 brightness*=0.01*percent_brightness; 02769 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue); 02770 } 02771 02772 static void ModulateHSL(const double percent_hue, 02773 const double percent_saturation,const double percent_lightness,double *red, 02774 double *green,double *blue) 02775 { 02776 double 02777 hue, 02778 lightness, 02779 saturation; 02780 02781 /* 02782 Increase or decrease color lightness, saturation, or hue. 02783 */ 02784 assert(red != (double *) NULL); 02785 assert(green != (double *) NULL); 02786 assert(blue != (double *) NULL); 02787 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness); 02788 hue+=0.5*(0.01*percent_hue-1.0); 02789 while (hue < 0.0) 02790 hue+=1.0; 02791 while (hue > 1.0) 02792 hue-=1.0; 02793 saturation*=0.01*percent_saturation; 02794 lightness*=0.01*percent_lightness; 02795 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue); 02796 } 02797 02798 static void ModulateHWB(const double percent_hue,const double percent_whiteness, const double percent_blackness,double *red,double *green,double *blue) 02799 { 02800 double 02801 blackness, 02802 hue, 02803 whiteness; 02804 02805 /* 02806 Increase or decrease color blackness, whiteness, or hue. 02807 */ 02808 assert(red != (double *) NULL); 02809 assert(green != (double *) NULL); 02810 assert(blue != (double *) NULL); 02811 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness); 02812 hue+=0.5*(0.01*percent_hue-1.0); 02813 while (hue < 0.0) 02814 hue+=1.0; 02815 while (hue > 1.0) 02816 hue-=1.0; 02817 blackness*=0.01*percent_blackness; 02818 whiteness*=0.01*percent_whiteness; 02819 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue); 02820 } 02821 02822 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate, 02823 ExceptionInfo *exception) 02824 { 02825 #define ModulateImageTag "Modulate/Image" 02826 02827 CacheView 02828 *image_view; 02829 02830 ColorspaceType 02831 colorspace; 02832 02833 const char 02834 *artifact; 02835 02836 double 02837 percent_brightness, 02838 percent_hue, 02839 percent_saturation; 02840 02841 GeometryInfo 02842 geometry_info; 02843 02844 MagickBooleanType 02845 status; 02846 02847 MagickOffsetType 02848 progress; 02849 02850 MagickStatusType 02851 flags; 02852 02853 register ssize_t 02854 i; 02855 02856 ssize_t 02857 y; 02858 02859 /* 02860 Initialize modulate table. 02861 */ 02862 assert(image != (Image *) NULL); 02863 assert(image->signature == MagickSignature); 02864 if (image->debug != MagickFalse) 02865 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02866 if (modulate == (char *) NULL) 02867 return(MagickFalse); 02868 flags=ParseGeometry(modulate,&geometry_info); 02869 percent_brightness=geometry_info.rho; 02870 percent_saturation=geometry_info.sigma; 02871 if ((flags & SigmaValue) == 0) 02872 percent_saturation=100.0; 02873 percent_hue=geometry_info.xi; 02874 if ((flags & XiValue) == 0) 02875 percent_hue=100.0; 02876 colorspace=UndefinedColorspace; 02877 artifact=GetImageArtifact(image,"modulate:colorspace"); 02878 if (artifact != (const char *) NULL) 02879 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions, 02880 MagickFalse,artifact); 02881 if (image->storage_class == PseudoClass) 02882 { 02883 /* 02884 Modulate colormap. 02885 */ 02886 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02887 #pragma omp parallel for schedule(static,4) shared(progress,status) 02888 #endif 02889 for (i=0; i < (ssize_t) image->colors; i++) 02890 switch (colorspace) 02891 { 02892 case HSBColorspace: 02893 { 02894 ModulateHSB(percent_hue,percent_saturation,percent_brightness, 02895 &image->colormap[i].red,&image->colormap[i].green, 02896 &image->colormap[i].blue); 02897 break; 02898 } 02899 case HSLColorspace: 02900 default: 02901 { 02902 ModulateHSL(percent_hue,percent_saturation,percent_brightness, 02903 &image->colormap[i].red,&image->colormap[i].green, 02904 &image->colormap[i].blue); 02905 break; 02906 } 02907 case HWBColorspace: 02908 { 02909 ModulateHWB(percent_hue,percent_saturation,percent_brightness, 02910 &image->colormap[i].red,&image->colormap[i].green, 02911 &image->colormap[i].blue); 02912 break; 02913 } 02914 } 02915 } 02916 /* 02917 Modulate image. 02918 */ 02919 status=MagickTrue; 02920 progress=0; 02921 image_view=AcquireCacheView(image); 02922 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02923 #pragma omp parallel for schedule(static,4) shared(progress,status) 02924 #endif 02925 for (y=0; y < (ssize_t) image->rows; y++) 02926 { 02927 double 02928 blue, 02929 green, 02930 red; 02931 02932 register Quantum 02933 *restrict q; 02934 02935 register ssize_t 02936 x; 02937 02938 if (status == MagickFalse) 02939 continue; 02940 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02941 if (q == (Quantum *) NULL) 02942 { 02943 status=MagickFalse; 02944 continue; 02945 } 02946 for (x=0; x < (ssize_t) image->columns; x++) 02947 { 02948 red=(double) GetPixelRed(image,q); 02949 green=(double) GetPixelGreen(image,q); 02950 blue=(double) GetPixelBlue(image,q); 02951 switch (colorspace) 02952 { 02953 case HSBColorspace: 02954 { 02955 ModulateHSB(percent_hue,percent_saturation,percent_brightness, 02956 &red,&green,&blue); 02957 break; 02958 } 02959 case HSLColorspace: 02960 default: 02961 { 02962 ModulateHSL(percent_hue,percent_saturation,percent_brightness, 02963 &red,&green,&blue); 02964 break; 02965 } 02966 case HWBColorspace: 02967 { 02968 ModulateHWB(percent_hue,percent_saturation,percent_brightness, 02969 &red,&green,&blue); 02970 break; 02971 } 02972 } 02973 SetPixelRed(image,ClampToQuantum(red),q); 02974 SetPixelGreen(image,ClampToQuantum(green),q); 02975 SetPixelBlue(image,ClampToQuantum(blue),q); 02976 q+=GetPixelChannels(image); 02977 } 02978 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02979 status=MagickFalse; 02980 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02981 { 02982 MagickBooleanType 02983 proceed; 02984 02985 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02986 #pragma omp critical (MagickCore_ModulateImage) 02987 #endif 02988 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows); 02989 if (proceed == MagickFalse) 02990 status=MagickFalse; 02991 } 02992 } 02993 image_view=DestroyCacheView(image_view); 02994 return(status); 02995 } 02996 02997 /* 02998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02999 % % 03000 % % 03001 % % 03002 % N e g a t e I m a g e % 03003 % % 03004 % % 03005 % % 03006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03007 % 03008 % NegateImage() negates the colors in the reference image. The grayscale 03009 % option means that only grayscale values within the image are negated. 03010 % 03011 % The format of the NegateImage method is: 03012 % 03013 % MagickBooleanType NegateImage(Image *image, 03014 % const MagickBooleanType grayscale,ExceptionInfo *exception) 03015 % 03016 % A description of each parameter follows: 03017 % 03018 % o image: the image. 03019 % 03020 % o grayscale: If MagickTrue, only negate grayscale pixels within the image. 03021 % 03022 % o exception: return any errors or warnings in this structure. 03023 % 03024 */ 03025 MagickExport MagickBooleanType NegateImage(Image *image, 03026 const MagickBooleanType grayscale,ExceptionInfo *exception) 03027 { 03028 #define NegateImageTag "Negate/Image" 03029 03030 CacheView 03031 *image_view; 03032 03033 MagickBooleanType 03034 status; 03035 03036 MagickOffsetType 03037 progress; 03038 03039 register ssize_t 03040 i; 03041 03042 ssize_t 03043 y; 03044 03045 assert(image != (Image *) NULL); 03046 assert(image->signature == MagickSignature); 03047 if (image->debug != MagickFalse) 03048 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03049 if (image->storage_class == PseudoClass) 03050 { 03051 /* 03052 Negate colormap. 03053 */ 03054 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03055 #pragma omp parallel for schedule(static) shared(progress,status) 03056 #endif 03057 for (i=0; i < (ssize_t) image->colors; i++) 03058 { 03059 if (grayscale != MagickFalse) 03060 if ((image->colormap[i].red != image->colormap[i].green) || 03061 (image->colormap[i].green != image->colormap[i].blue)) 03062 continue; 03063 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 03064 image->colormap[i].red=(Quantum) QuantumRange- 03065 image->colormap[i].red; 03066 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 03067 image->colormap[i].green=(Quantum) QuantumRange- 03068 image->colormap[i].green; 03069 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 03070 image->colormap[i].blue=(Quantum) QuantumRange- 03071 image->colormap[i].blue; 03072 } 03073 } 03074 /* 03075 Negate image. 03076 */ 03077 status=MagickTrue; 03078 progress=0; 03079 image_view=AcquireCacheView(image); 03080 if (grayscale != MagickFalse) 03081 { 03082 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03083 #pragma omp parallel for schedule(static) shared(progress,status) 03084 #endif 03085 for (y=0; y < (ssize_t) image->rows; y++) 03086 { 03087 MagickBooleanType 03088 sync; 03089 03090 register Quantum 03091 *restrict q; 03092 03093 register ssize_t 03094 x; 03095 03096 if (status == MagickFalse) 03097 continue; 03098 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 03099 exception); 03100 if (q == (Quantum *) NULL) 03101 { 03102 status=MagickFalse; 03103 continue; 03104 } 03105 for (x=0; x < (ssize_t) image->columns; x++) 03106 { 03107 register ssize_t 03108 i; 03109 03110 if ((GetPixelMask(image,q) != 0) || 03111 (IsPixelGray(image,q) != MagickFalse)) 03112 { 03113 q+=GetPixelChannels(image); 03114 continue; 03115 } 03116 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03117 { 03118 PixelChannel 03119 channel; 03120 03121 PixelTrait 03122 traits; 03123 03124 channel=GetPixelChannelMapChannel(image,i); 03125 traits=GetPixelChannelMapTraits(image,channel); 03126 if ((traits & UpdatePixelTrait) == 0) 03127 continue; 03128 q[i]=QuantumRange-q[i]; 03129 } 03130 q+=GetPixelChannels(image); 03131 } 03132 sync=SyncCacheViewAuthenticPixels(image_view,exception); 03133 if (sync == MagickFalse) 03134 status=MagickFalse; 03135 if (image->progress_monitor != (MagickProgressMonitor) NULL) 03136 { 03137 MagickBooleanType 03138 proceed; 03139 03140 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03141 #pragma omp critical (MagickCore_NegateImage) 03142 #endif 03143 proceed=SetImageProgress(image,NegateImageTag,progress++, 03144 image->rows); 03145 if (proceed == MagickFalse) 03146 status=MagickFalse; 03147 } 03148 } 03149 image_view=DestroyCacheView(image_view); 03150 return(MagickTrue); 03151 } 03152 /* 03153 Negate image. 03154 */ 03155 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03156 #pragma omp parallel for schedule(static) shared(progress,status) 03157 #endif 03158 for (y=0; y < (ssize_t) image->rows; y++) 03159 { 03160 register Quantum 03161 *restrict q; 03162 03163 register ssize_t 03164 x; 03165 03166 if (status == MagickFalse) 03167 continue; 03168 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 03169 if (q == (Quantum *) NULL) 03170 { 03171 status=MagickFalse; 03172 continue; 03173 } 03174 for (x=0; x < (ssize_t) image->columns; x++) 03175 { 03176 register ssize_t 03177 i; 03178 03179 if (GetPixelMask(image,q) != 0) 03180 { 03181 q+=GetPixelChannels(image); 03182 continue; 03183 } 03184 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03185 { 03186 PixelChannel 03187 channel; 03188 03189 PixelTrait 03190 traits; 03191 03192 channel=GetPixelChannelMapChannel(image,i); 03193 traits=GetPixelChannelMapTraits(image,channel); 03194 if ((traits & UpdatePixelTrait) == 0) 03195 continue; 03196 q[i]=QuantumRange-q[i]; 03197 } 03198 q+=GetPixelChannels(image); 03199 } 03200 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 03201 status=MagickFalse; 03202 if (image->progress_monitor != (MagickProgressMonitor) NULL) 03203 { 03204 MagickBooleanType 03205 proceed; 03206 03207 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03208 #pragma omp critical (MagickCore_NegateImage) 03209 #endif 03210 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows); 03211 if (proceed == MagickFalse) 03212 status=MagickFalse; 03213 } 03214 } 03215 image_view=DestroyCacheView(image_view); 03216 return(status); 03217 } 03218 03219 /* 03220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03221 % % 03222 % % 03223 % % 03224 % N o r m a l i z e I m a g e % 03225 % % 03226 % % 03227 % % 03228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03229 % 03230 % NormalizeImage() enhances the contrast of a color image by mapping the 03231 % darkest 2 percent of all pixel to black and the brightest 1 percent to white. 03232 % 03233 % The format of the NormalizeImage method is: 03234 % 03235 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception) 03236 % 03237 % A description of each parameter follows: 03238 % 03239 % o image: the image. 03240 % 03241 % o exception: return any errors or warnings in this structure. 03242 % 03243 */ 03244 MagickExport MagickBooleanType NormalizeImage(Image *image, 03245 ExceptionInfo *exception) 03246 { 03247 double 03248 black_point, 03249 white_point; 03250 03251 black_point=(double) image->columns*image->rows*0.0015; 03252 white_point=(double) image->columns*image->rows*0.9995; 03253 return(ContrastStretchImage(image,black_point,white_point,exception)); 03254 } 03255 03256 /* 03257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03258 % % 03259 % % 03260 % % 03261 % S i g m o i d a l C o n t r a s t I m a g e % 03262 % % 03263 % % 03264 % % 03265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03266 % 03267 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear 03268 % sigmoidal contrast algorithm. Increase the contrast of the image using a 03269 % sigmoidal transfer function without saturating highlights or shadows. 03270 % Contrast indicates how much to increase the contrast (0 is none; 3 is 03271 % typical; 20 is pushing it); mid-point indicates where threshold 'knee' of 03272 % the curve falls (typical 50% for mid-gray). Set sharpen to MagickTrue to 03273 % increase the image contrast otherwise the contrast is reduced. 03274 % 03275 % The format of the SigmoidalContrastImage method is: 03276 % 03277 % MagickBooleanType SigmoidalContrastImage(Image *image, 03278 % const MagickBooleanType sharpen,const char *levels, 03279 % ExceptionInfo *exception) 03280 % 03281 % A description of each parameter follows: 03282 % 03283 % o image: the image. 03284 % 03285 % o sharpen: Increase or decrease image contrast. 03286 % 03287 % o alpha: strength of the contrast, the larger the number the more 03288 % 'threshold-like' it becomes. 03289 % 03290 % o beta: midpoint of the function as a color value 0 to QuantumRange. 03291 % 03292 % o exception: return any errors or warnings in this structure. 03293 % 03294 */ 03295 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image, 03296 const MagickBooleanType sharpen,const double contrast,const double midpoint, 03297 ExceptionInfo *exception) 03298 { 03299 #define SigmoidalContrastImageTag "SigmoidalContrast/Image" 03300 03301 CacheView 03302 *image_view; 03303 03304 MagickBooleanType 03305 status; 03306 03307 MagickOffsetType 03308 progress; 03309 03310 MagickRealType 03311 *sigmoidal_map; 03312 03313 register ssize_t 03314 i; 03315 03316 ssize_t 03317 y; 03318 03319 /* 03320 Allocate and initialize sigmoidal maps. 03321 */ 03322 assert(image != (Image *) NULL); 03323 assert(image->signature == MagickSignature); 03324 if (image->debug != MagickFalse) 03325 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03326 sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL, 03327 sizeof(*sigmoidal_map)); 03328 if (sigmoidal_map == (MagickRealType *) NULL) 03329 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03330 image->filename); 03331 (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map)); 03332 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03333 #pragma omp parallel for schedule(static) shared(progress,status) 03334 #endif 03335 for (i=0; i <= (ssize_t) MaxMap; i++) 03336 { 03337 if (sharpen != MagickFalse) 03338 { 03339 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType) 03340 (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange- 03341 (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/(double) 03342 QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/(double) 03343 QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/(double) 03344 QuantumRange)))))+0.5)); 03345 continue; 03346 } 03347 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType) 03348 (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/(double) 03349 QuantumRange*contrast))+((double) i/MaxMap)*((1.0/(1.0+exp(contrast*( 03350 midpoint/(double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/(double) 03351 QuantumRange*contrast))))))/(1.0/(1.0+exp(midpoint/(double) QuantumRange* 03352 contrast))+((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/(double) 03353 QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/(double) QuantumRange* 03354 contrast))))))/contrast))); 03355 } 03356 if (image->storage_class == PseudoClass) 03357 { 03358 /* 03359 Sigmoidal-contrast enhance colormap. 03360 */ 03361 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03362 #pragma omp parallel for schedule(static,4) shared(progress,status) 03363 #endif 03364 for (i=0; i < (ssize_t) image->colors; i++) 03365 { 03366 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 03367 image->colormap[i].red=sigmoidal_map[ScaleQuantumToMap( 03368 ClampToQuantum(image->colormap[i].red))]; 03369 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 03370 image->colormap[i].green=sigmoidal_map[ScaleQuantumToMap( 03371 ClampToQuantum(image->colormap[i].green))]; 03372 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 03373 image->colormap[i].blue=sigmoidal_map[ScaleQuantumToMap( 03374 ClampToQuantum(image->colormap[i].blue))]; 03375 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 03376 image->colormap[i].alpha=sigmoidal_map[ScaleQuantumToMap( 03377 ClampToQuantum(image->colormap[i].alpha))]; 03378 } 03379 } 03380 /* 03381 Sigmoidal-contrast enhance image. 03382 */ 03383 status=MagickTrue; 03384 progress=0; 03385 image_view=AcquireCacheView(image); 03386 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03387 #pragma omp parallel for schedule(static,4) shared(progress,status) 03388 #endif 03389 for (y=0; y < (ssize_t) image->rows; y++) 03390 { 03391 register Quantum 03392 *restrict q; 03393 03394 register ssize_t 03395 x; 03396 03397 if (status == MagickFalse) 03398 continue; 03399 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 03400 if (q == (Quantum *) NULL) 03401 { 03402 status=MagickFalse; 03403 continue; 03404 } 03405 for (x=0; x < (ssize_t) image->columns; x++) 03406 { 03407 register ssize_t 03408 i; 03409 03410 if (GetPixelMask(image,q) != 0) 03411 { 03412 q+=GetPixelChannels(image); 03413 continue; 03414 } 03415 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03416 { 03417 PixelChannel 03418 channel; 03419 03420 PixelTrait 03421 traits; 03422 03423 channel=GetPixelChannelMapChannel(image,i); 03424 traits=GetPixelChannelMapTraits(image,channel); 03425 if ((traits & UpdatePixelTrait) == 0) 03426 continue; 03427 q[i]=ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(q[i])]); 03428 } 03429 q+=GetPixelChannels(image); 03430 } 03431 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 03432 status=MagickFalse; 03433 if (image->progress_monitor != (MagickProgressMonitor) NULL) 03434 { 03435 MagickBooleanType 03436 proceed; 03437 03438 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03439 #pragma omp critical (MagickCore_SigmoidalContrastImage) 03440 #endif 03441 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++, 03442 image->rows); 03443 if (proceed == MagickFalse) 03444 status=MagickFalse; 03445 } 03446 } 03447 image_view=DestroyCacheView(image_view); 03448 sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map); 03449 return(status); 03450 }