MagickCore  6.7.5
enhance.c
Go to the documentation of this file.
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 }