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-2008 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 "magick/studio.h"
00044 #include "magick/cache.h"
00045 #include "magick/cache-view.h"
00046 #include "magick/color.h"
00047 #include "magick/color-private.h"
00048 #include "magick/colorspace.h"
00049 #include "magick/enhance.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/gem.h"
00053 #include "magick/geometry.h"
00054 #include "magick/image.h"
00055 #include "magick/image-private.h"
00056 #include "magick/memory_.h"
00057 #include "magick/monitor.h"
00058 #include "magick/monitor-private.h"
00059 #include "magick/quantum.h"
00060 #include "magick/quantum-private.h"
00061 #include "magick/resample.h"
00062 #include "magick/resample-private.h"
00063 #include "magick/string_.h"
00064 
00065 /*
00066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00067 %                                                                             %
00068 %                                                                             %
00069 %                                                                             %
00070 %     C l u t I m a g e                                                       %
00071 %                                                                             %
00072 %                                                                             %
00073 %                                                                             %
00074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00075 %
00076 %  ClutImage() replaces colors in the image from a color lookup table.
00077 %
00078 %  The format of the ClutImage method is:
00079 %
00080 %      MagickBooleanType ClutImage(Image *image,Image *clut_image)
00081 %      MagickBooleanType ClutImageChannel(Image *image,
00082 %        const ChannelType channel,Image *clut_image)
00083 %
00084 %  A description of each parameter follows:
00085 %
00086 %    o image: the image.
00087 %
00088 %    o channel: the channel.
00089 %
00090 %    o clut_image: the color lookup.
00091 %
00092 */
00093 
00094 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
00095 {
00096   return(ClutImageChannel(image,DefaultChannels,clut_image));
00097 }
00098 
00099 MagickExport MagickBooleanType ClutImageChannel(Image *image,
00100   const ChannelType channel,const Image *clut_image)
00101 {
00102 #define ClutImageTag  "Clut/Image"
00103 
00104   ExceptionInfo
00105     *exception;
00106 
00107   long
00108     adjust,
00109     progress,
00110     y;
00111 
00112   MagickBooleanType
00113     status;
00114 
00115   MagickPixelPacket
00116     zero;
00117 
00118   ResampleFilter
00119     **resample_filter;
00120 
00121   ViewInfo
00122     **image_view;
00123 
00124   assert(image != (Image *) NULL);
00125   assert(image->signature == MagickSignature);
00126   if (image->debug != MagickFalse)
00127     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00128   assert(clut_image != (Image *) NULL);
00129   assert(clut_image->signature == MagickSignature);
00130   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00131     return(MagickFalse);
00132   /*
00133     Clut image.
00134   */
00135   status=MagickTrue;
00136   progress=0;
00137   GetMagickPixelPacket(clut_image,&zero);
00138   adjust=clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1;
00139   exception=(&image->exception);
00140   resample_filter=AcquireResampleFilterThreadSet(clut_image,MagickTrue,
00141     exception);
00142   image_view=AcquireCacheViewThreadSet(image);
00143 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00144   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00145 #endif
00146   for (y=0; y < (long) image->rows; y++)
00147   {
00148     MagickPixelPacket
00149       pixel;
00150 
00151     register IndexPacket
00152       *indexes;
00153 
00154     register long
00155       id,
00156       x;
00157 
00158     register PixelPacket
00159       *q;
00160 
00161     if (status == MagickFalse)
00162       continue;
00163     id=GetCacheViewThreadId();
00164     q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00165       exception);
00166     if (q == (PixelPacket *) NULL)
00167       {
00168         status=MagickFalse;
00169         continue;
00170       }
00171     indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00172     pixel=zero;
00173     for (x=0; x < (long) image->columns; x++)
00174     {
00175       if ((channel & RedChannel) != 0)
00176         {
00177           (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->red*
00178             (clut_image->columns-adjust),QuantumScale*q->red*
00179             (clut_image->rows-adjust),&pixel);
00180           q->red=RoundToQuantum(pixel.red);
00181         }
00182       if ((channel & GreenChannel) != 0)
00183         {
00184           (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->green*
00185             (clut_image->columns-adjust),QuantumScale*q->green*
00186             (clut_image->rows-adjust),&pixel);
00187           q->green=RoundToQuantum(pixel.green);
00188         }
00189       if ((channel & BlueChannel) != 0)
00190         {
00191           (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->blue*
00192             (clut_image->columns-adjust),QuantumScale*q->blue*
00193             (clut_image->rows-adjust),&pixel);
00194           q->blue=RoundToQuantum(pixel.blue);
00195         }
00196       if ((channel & OpacityChannel) != 0)
00197         {
00198           if (clut_image->matte == MagickFalse)
00199             {
00200               /*
00201                 A gray-scale LUT replacement for an image alpha channel.
00202               */
00203               (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00204                 (QuantumRange-q->opacity)*(clut_image->columns+adjust),
00205                 QuantumScale*(QuantumRange-q->opacity)*(clut_image->rows+
00206                 adjust),&pixel);
00207               q->opacity=(Quantum) (QuantumRange-MagickPixelIntensityToQuantum(
00208                 &pixel));
00209             }
00210           else
00211             if (image->matte == MagickFalse)
00212               {
00213                 /*
00214                   A greyscale image being colored by a LUT with transparency.
00215                 */
00216                 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00217                   PixelIntensity(q)*(clut_image->columns-adjust),QuantumScale*
00218                   PixelIntensity(q)*(clut_image->rows-adjust),&pixel);
00219                 q->opacity=RoundToQuantum(pixel.opacity);
00220               }
00221             else
00222               {
00223                 /*
00224                   Direct alpha channel lookup.
00225                 */
00226                 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00227                   q->opacity*(clut_image->columns-adjust),QuantumScale*
00228                   q->opacity* (clut_image->rows-adjust),&pixel);
00229                 q->opacity=RoundToQuantum(pixel.opacity);
00230               }
00231         }
00232       if (((channel & IndexChannel) != 0) &&
00233           (image->colorspace == CMYKColorspace))
00234         {
00235           (void) ResamplePixelColor(resample_filter[id],QuantumScale*indexes[x]*
00236             (clut_image->columns-adjust),QuantumScale*indexes[x]*
00237             (clut_image->rows-adjust),&pixel);
00238           indexes[x]=RoundToQuantum(pixel.index);
00239         }
00240       q++;
00241     }
00242     if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00243       status=MagickFalse;
00244     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00245       {
00246         MagickBooleanType
00247           proceed;
00248 
00249 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00250   #pragma omp critical
00251 #endif
00252         proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
00253         if (proceed == MagickFalse)
00254           status=MagickFalse;
00255       }
00256   }
00257   image_view=DestroyCacheViewThreadSet(image_view);
00258   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
00259   /*
00260     Enable alpha channel if CLUT image could enable it.
00261   */
00262   if ((clut_image->matte == MagickTrue) && ((channel & OpacityChannel) != 0))
00263     (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
00264   return(status);
00265 }
00266 
00267 /*
00268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00269 %                                                                             %
00270 %                                                                             %
00271 %                                                                             %
00272 %     C o n t r a s t I m a g e                                               %
00273 %                                                                             %
00274 %                                                                             %
00275 %                                                                             %
00276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00277 %
00278 %  ContrastImage() enhances the intensity differences between the lighter and
00279 %  darker elements of the image.  Set sharpen to a MagickTrue to increase the
00280 %  image contrast otherwise the contrast is reduced.
00281 %
00282 %  The format of the ContrastImage method is:
00283 %
00284 %      MagickBooleanType ContrastImage(Image *image,
00285 %        const MagickBooleanType sharpen)
00286 %
00287 %  A description of each parameter follows:
00288 %
00289 %    o image: the image.
00290 %
00291 %    o sharpen: Increase or decrease image contrast.
00292 %
00293 */
00294 
00295 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
00296 {
00297   double
00298     brightness,
00299     hue,
00300     saturation;
00301 
00302   /*
00303     Enhance contrast: dark color become darker, light color become lighter.
00304   */
00305   assert(red != (Quantum *) NULL);
00306   assert(green != (Quantum *) NULL);
00307   assert(blue != (Quantum *) NULL);
00308   hue=0.0;
00309   saturation=0.0;
00310   brightness=0.0;
00311   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
00312   brightness+=0.5*sign*(0.5*(sin(MagickPI*(brightness-0.5))+1.0)-brightness);
00313   if (brightness > 1.0)
00314     brightness=1.0;
00315   else
00316     if (brightness < 0.0)
00317       brightness=0.0;
00318   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
00319 }
00320 
00321 MagickExport MagickBooleanType ContrastImage(Image *image,
00322   const MagickBooleanType sharpen)
00323 {
00324 #define ContrastImageTag  "Contrast/Image"
00325 
00326   ExceptionInfo
00327     *exception;
00328 
00329   int
00330     sign;
00331 
00332   long
00333     progress,
00334     y;
00335 
00336   MagickBooleanType
00337     status;
00338 
00339   register long
00340     i;
00341 
00342   ViewInfo
00343     **image_view;
00344 
00345   assert(image != (Image *) NULL);
00346   assert(image->signature == MagickSignature);
00347   if (image->debug != MagickFalse)
00348     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00349   sign=sharpen != MagickFalse ? 1 : -1;
00350   if (image->storage_class == PseudoClass)
00351     {
00352       /*
00353         Contrast enhance colormap.
00354       */
00355       for (i=0; i < (long) image->colors; i++)
00356         Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
00357           &image->colormap[i].blue);
00358     }
00359   /*
00360     Contrast enhance image.
00361   */
00362   status=MagickTrue;
00363   progress=0;
00364   exception=(&image->exception);
00365   image_view=AcquireCacheViewThreadSet(image);
00366 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00367   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00368 #endif
00369   for (y=0; y < (long) image->rows; y++)
00370   {
00371     register long
00372       id,
00373       x;
00374 
00375     register PixelPacket
00376       *q;
00377 
00378     if (status == MagickFalse)
00379       continue;
00380     id=GetCacheViewThreadId();
00381     q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00382       exception);
00383     if (q == (PixelPacket *) NULL)
00384       {
00385         status=MagickFalse;
00386         continue;
00387       }
00388     for (x=0; x < (long) image->columns; x++)
00389     {
00390       Contrast(sign,&q->red,&q->green,&q->blue);
00391       q++;
00392     }
00393     if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00394       status=MagickFalse;
00395     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00396       {
00397         MagickBooleanType
00398           proceed;
00399 
00400 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00401   #pragma omp critical
00402 #endif
00403         proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
00404         if (proceed == MagickFalse)
00405           status=MagickFalse;
00406       }
00407   }
00408   image_view=DestroyCacheViewThreadSet(image_view);
00409   return(status);
00410 }
00411 
00412 /*
00413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00414 %                                                                             %
00415 %                                                                             %
00416 %                                                                             %
00417 %     C o n t r a s t S t r e t c h I m a g e                                 %
00418 %                                                                             %
00419 %                                                                             %
00420 %                                                                             %
00421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00422 %
00423 %  The ContrastStretchImage() is a simple image enhancement technique that
00424 %  attempts to improve the contrast in an image by `stretching' the range of
00425 %  intensity values it contains to span a desired range of values. It differs
00426 %  from the more sophisticated histogram equalization in that it can only
00427 %  apply %  a linear scaling function to the image pixel values.  As a result
00428 %  the `enhancement' is less harsh.
00429 %
00430 %  The format of the ContrastStretchImage method is:
00431 %
00432 %      MagickBooleanType ContrastStretchImage(Image *image,
00433 %        const char *levels)
00434 %      MagickBooleanType ContrastStretchImageChannel(Image *image,
00435 %        const unsigned long channel,const double black_point,
00436 %        const double white_point)
00437 %
00438 %  A description of each parameter follows:
00439 %
00440 %    o image: the image.
00441 %
00442 %    o channel: the channel.
00443 %
00444 %    o black_point: the black point.
00445 %
00446 %    o white_point: the white point.
00447 %
00448 %    o levels: Specify the levels where the black and white points have the
00449 %      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
00450 %
00451 */
00452 
00453 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
00454   const char *levels)
00455 {
00456   double
00457     black_point,
00458     white_point;
00459 
00460   GeometryInfo
00461     geometry_info;
00462 
00463   MagickBooleanType
00464     status;
00465 
00466   MagickStatusType
00467     flags;
00468 
00469   /*
00470     Parse levels.
00471   */
00472   if (levels == (char *) NULL)
00473     return(MagickFalse);
00474   flags=ParseGeometry(levels,&geometry_info);
00475   black_point=geometry_info.rho;
00476   white_point=(double) image->columns*image->rows;
00477   if ((flags & SigmaValue) != 0)
00478     white_point=geometry_info.sigma;
00479   if ((flags & PercentValue) != 0)
00480     {
00481       black_point*=(double) QuantumRange/100.0;
00482       white_point*=(double) QuantumRange/100.0;
00483     }
00484   if ((flags & SigmaValue) == 0)
00485     white_point=(double) image->columns*image->rows-black_point;
00486   status=ContrastStretchImageChannel(image,DefaultChannels,black_point,
00487     white_point);
00488   return(status);
00489 }
00490 
00491 MagickExport MagickBooleanType ContrastStretchImageChannel(Image *image,
00492   const ChannelType channel,const double black_point,const double white_point)
00493 {
00494 #define MaxRange(color)  ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
00495 #define ContrastStretchImageTag  "ContrastStretch/Image"
00496 
00497   double
00498     intensity;
00499 
00500   ExceptionInfo
00501     *exception;
00502 
00503   long
00504     progress,
00505     y;
00506 
00507   MagickBooleanType
00508     status;
00509 
00510   MagickPixelPacket
00511     black,
00512     *histogram,
00513     *stretch_map,
00514     white;
00515 
00516   register long
00517     i;
00518 
00519   ViewInfo
00520     **image_view;
00521 
00522   /*
00523     Allocate histogram and stretch map.
00524   */
00525   assert(image != (Image *) NULL);
00526   assert(image->signature == MagickSignature);
00527   if (image->debug != MagickFalse)
00528     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00529   histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
00530     sizeof(*histogram));
00531   stretch_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
00532     sizeof(*stretch_map));
00533   if ((histogram == (MagickPixelPacket *) NULL) ||
00534       (stretch_map == (MagickPixelPacket *) NULL))
00535     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00536       image->filename);
00537   /*
00538     Form histogram.
00539   */
00540   status=MagickTrue;
00541   exception=(&image->exception);
00542   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
00543   image_view=AcquireCacheViewThreadSet(image);
00544   for (y=0; y < (long) image->rows; y++)
00545   {
00546     IndexPacket
00547       *indexes;
00548 
00549     register const PixelPacket
00550       *p;
00551 
00552     register long
00553       id,
00554       x;
00555 
00556     if (status == MagickFalse)
00557       continue;
00558     id=GetCacheViewThreadId();
00559     p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00560     if (p == (const PixelPacket *) NULL)
00561       {
00562         status=MagickFalse;
00563         continue;
00564       }
00565     indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00566     if (channel == DefaultChannels)
00567       for (x=0; x < (long) image->columns; x++)
00568       {
00569         Quantum
00570           intensity;
00571 
00572         intensity=PixelIntensityToQuantum(p+x);
00573         histogram[ScaleQuantumToMap(intensity)].red++;
00574         histogram[ScaleQuantumToMap(intensity)].green++;
00575         histogram[ScaleQuantumToMap(intensity)].blue++;
00576         histogram[ScaleQuantumToMap(intensity)].index++;
00577       }
00578     else
00579       for (x=0; x < (long) image->columns; x++)
00580       {
00581         if ((channel & RedChannel) != 0)
00582           histogram[ScaleQuantumToMap((p+x)->red)].red++;
00583         if ((channel & GreenChannel) != 0)
00584           histogram[ScaleQuantumToMap((p+x)->green)].green++;
00585         if ((channel & BlueChannel) != 0)
00586           histogram[ScaleQuantumToMap((p+x)->blue)].blue++;
00587         if ((channel & OpacityChannel) != 0)
00588           histogram[ScaleQuantumToMap((p+x)->opacity)].opacity++;
00589         if (((channel & IndexChannel) != 0) &&
00590             (image->colorspace == CMYKColorspace))
00591           histogram[ScaleQuantumToMap(indexes[x])].index++;
00592       }
00593   }
00594   /*
00595     Find the histogram boundaries by locating the black/white levels.
00596   */
00597   black.red=0.0;
00598   white.red=MaxRange(QuantumRange);
00599   if ((channel & RedChannel) != 0)
00600     {
00601       intensity=0.0;
00602       for (i=0; i <= (long) MaxMap; i++)
00603       {
00604         intensity+=histogram[i].red;
00605         if (intensity > black_point)
00606           break;
00607       }
00608       black.red=(MagickRealType) i;
00609       intensity=0.0;
00610       for (i=(long) MaxMap; i != 0; i--)
00611       {
00612         intensity+=histogram[i].red;
00613         if (intensity > ((double) image->columns*image->rows-white_point))
00614           break;
00615       }
00616       white.red=(MagickRealType) i;
00617     }
00618   black.green=0.0;
00619   white.green=MaxRange(QuantumRange);
00620   if ((channel & GreenChannel) != 0)
00621     {
00622       intensity=0.0;
00623       for (i=0; i <= (long) MaxMap; i++)
00624       {
00625         intensity+=histogram[i].green;
00626         if (intensity > black_point)
00627           break;
00628       }
00629       black.green=(MagickRealType) i;
00630       intensity=0.0;
00631       for (i=(long) MaxMap; i != 0; i--)
00632       {
00633         intensity+=histogram[i].green;
00634         if (intensity > ((double) image->columns*image->rows-white_point))
00635           break;
00636       }
00637       white.green=(MagickRealType) i;
00638     }
00639   black.blue=0.0;
00640   white.blue=MaxRange(QuantumRange);
00641   if ((channel & BlueChannel) != 0)
00642     {
00643       intensity=0.0;
00644       for (i=0; i <= (long) MaxMap; i++)
00645       {
00646         intensity+=histogram[i].blue;
00647         if (intensity > black_point)
00648           break;
00649       }
00650       black.blue=(MagickRealType) i;
00651       intensity=0.0;
00652       for (i=(long) MaxMap; i != 0; i--)
00653       {
00654         intensity+=histogram[i].blue;
00655         if (intensity > ((double) image->columns*image->rows-white_point))
00656           break;
00657       }
00658       white.blue=(MagickRealType) i;
00659     }
00660   black.opacity=0.0;
00661   white.opacity=MaxRange(QuantumRange);
00662   if ((channel & OpacityChannel) != 0)
00663     {
00664       intensity=0.0;
00665       for (i=0; i <= (long) MaxMap; i++)
00666       {
00667         intensity+=histogram[i].opacity;
00668         if (intensity > black_point)
00669           break;
00670       }
00671       black.opacity=(MagickRealType) i;
00672       intensity=0.0;
00673       for (i=(long) MaxMap; i != 0; i--)
00674       {
00675         intensity+=histogram[i].opacity;
00676         if (intensity > ((double) image->columns*image->rows-white_point))
00677           break;
00678       }
00679       white.opacity=(MagickRealType) i;
00680     }
00681   black.index=0.0;
00682   white.index=MaxRange(QuantumRange);
00683   if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
00684     {
00685       intensity=0.0;
00686       for (i=0; i <= (long) MaxMap; i++)
00687       {
00688         intensity+=histogram[i].index;
00689         if (intensity > black_point)
00690           break;
00691       }
00692       black.index=(MagickRealType) i;
00693       intensity=0.0;
00694       for (i=(long) MaxMap; i != 0; i--)
00695       {
00696         intensity+=histogram[i].index;
00697         if (intensity > ((double) image->columns*image->rows-white_point))
00698           break;
00699       }
00700       white.index=(MagickRealType) i;
00701     }
00702   histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
00703   /*
00704     Stretch the histogram to create the stretched image mapping.
00705   */
00706   (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
00707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00708   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00709 #endif
00710   for (i=0; i <= (long) MaxMap; i++)
00711   {
00712     if ((channel & RedChannel) != 0)
00713       {
00714         if (i < (long) black.red)
00715           stretch_map[i].red=0.0;
00716         else
00717           if (i > (long) white.red)
00718             stretch_map[i].red=(MagickRealType) QuantumRange;
00719           else
00720             if (black.red != white.red)
00721               stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
00722                 (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
00723       }
00724     if ((channel & GreenChannel) != 0)
00725       {
00726         if (i < (long) black.green)
00727           stretch_map[i].green=0.0;
00728         else
00729           if (i > (long) white.green)
00730             stretch_map[i].green=(MagickRealType) QuantumRange;
00731           else
00732             if (black.green != white.green)
00733               stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
00734                 (MagickRealType) (MaxMap*(i-black.green)/(white.green-
00735                 black.green)));
00736       }
00737     if ((channel & BlueChannel) != 0)
00738       {
00739         if (i < (long) black.blue)
00740           stretch_map[i].blue=0.0;
00741         else
00742           if (i > (long) white.blue)
00743             stretch_map[i].blue=(MagickRealType) QuantumRange;
00744           else
00745             if (black.blue != white.blue)
00746               stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
00747                 (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
00748                 black.blue)));
00749       }
00750     if ((channel & OpacityChannel) != 0)
00751       {
00752         if (i < (long) black.opacity)
00753           stretch_map[i].opacity=0.0;
00754         else
00755           if (i > (long) white.opacity)
00756             stretch_map[i].opacity=(MagickRealType) QuantumRange;
00757           else
00758             if (black.opacity != white.opacity)
00759               stretch_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
00760                 (MagickRealType) (MaxMap*(i-black.opacity)/(white.opacity-
00761                 black.opacity)));
00762       }
00763     if (((channel & IndexChannel) != 0) &&
00764         (image->colorspace == CMYKColorspace))
00765       {
00766         if (i < (long) black.index)
00767           stretch_map[i].index=0.0;
00768         else
00769           if (i > (long) white.index)
00770             stretch_map[i].index=(MagickRealType) QuantumRange;
00771           else
00772             if (black.index != white.index)
00773               stretch_map[i].index=(MagickRealType) ScaleMapToQuantum(
00774                 (MagickRealType) (MaxMap*(i-black.index)/(white.index-
00775                 black.index)));
00776       }
00777   }
00778   /*
00779     Stretch the image.
00780   */
00781   if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
00782       (image->colorspace == CMYKColorspace)))
00783     image->storage_class=DirectClass;
00784   if (image->storage_class == PseudoClass)
00785     {
00786       /*
00787         Stretch colormap.
00788       */
00789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00790   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00791 #endif
00792       for (i=0; i < (long) image->colors; i++)
00793       {
00794         if ((channel & RedChannel) != 0)
00795           {
00796             if (black.red != white.red)
00797               image->colormap[i].red=RoundToQuantum(stretch_map[
00798                 ScaleQuantumToMap(image->colormap[i].red)].red);
00799           }
00800         if ((channel & GreenChannel) != 0)
00801           {
00802             if (black.green != white.green)
00803               image->colormap[i].green=RoundToQuantum(stretch_map[
00804                 ScaleQuantumToMap(image->colormap[i].green)].green);
00805           }
00806         if ((channel & BlueChannel) != 0)
00807           {
00808             if (black.blue != white.blue)
00809               image->colormap[i].blue=RoundToQuantum(stretch_map[
00810                 ScaleQuantumToMap(image->colormap[i].blue)].blue);
00811           }
00812         if ((channel & OpacityChannel) != 0)
00813           {
00814             if (black.opacity != white.opacity)
00815               image->colormap[i].opacity=RoundToQuantum(stretch_map[
00816                 ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
00817           }
00818       }
00819     }
00820   /*
00821     Stretch image.
00822   */
00823   status=MagickTrue;
00824   progress=0;
00825 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00826   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00827 #endif
00828   for (y=0; y < (long) image->rows; y++)
00829   {
00830     IndexPacket
00831       *indexes;
00832 
00833     register long
00834       id,
00835       x;
00836 
00837     register PixelPacket
00838       *q;
00839 
00840     if (status == MagickFalse)
00841       continue;
00842     id=GetCacheViewThreadId();
00843     q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00844       exception);
00845     if (q == (PixelPacket *) NULL)
00846       {
00847         status=MagickFalse;
00848         continue;
00849       }
00850     indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00851     for (x=0; x < (long) image->columns; x++)
00852     {
00853       if ((channel & RedChannel) != 0)
00854         {
00855           if (black.red != white.red)
00856             q->red=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00857               q->red)].red);
00858         }
00859       if ((channel & GreenChannel) != 0)
00860         {
00861           if (black.green != white.green)
00862             q->green=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00863               q->green)].green);
00864         }
00865       if ((channel & BlueChannel) != 0)
00866         {
00867           if (black.blue != white.blue)
00868             q->blue=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00869               q->blue)].blue);
00870         }
00871       if ((channel & OpacityChannel) != 0)
00872         {
00873           if (black.opacity != white.opacity)
00874             q->opacity=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00875               q->opacity)].opacity);
00876         }
00877       if (((channel & IndexChannel) != 0) &&
00878           (image->colorspace == CMYKColorspace))
00879         {
00880           if (black.index != white.index)
00881             indexes[x]=(IndexPacket) RoundToQuantum(stretch_map[
00882               ScaleQuantumToMap(indexes[x])].index);
00883         }
00884       q++;
00885     }
00886     if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00887       status=MagickFalse;
00888     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00889       {
00890         MagickBooleanType
00891           proceed;
00892 
00893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00894   #pragma omp critical
00895 #endif
00896         proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
00897           image->rows);
00898         if (proceed == MagickFalse)
00899           status=MagickFalse;
00900       }
00901   }
00902   image_view=DestroyCacheViewThreadSet(image_view);
00903   stretch_map=(MagickPixelPacket *) RelinquishMagickMemory(stretch_map);
00904   return(status);
00905 }
00906 
00907 /*
00908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00909 %                                                                             %
00910 %                                                                             %
00911 %                                                                             %
00912 %     E n h a n c e I m a g e                                                 %
00913 %                                                                             %
00914 %                                                                             %
00915 %                                                                             %
00916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00917 %
00918 %  EnhanceImage() applies a digital filter that improves the quality of a
00919 %  noisy image.
00920 %
00921 %  The format of the EnhanceImage method is:
00922 %
00923 %      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
00924 %
00925 %  A description of each parameter follows:
00926 %
00927 %    o image: the image.
00928 %
00929 %    o exception: return any errors or warnings in this structure.
00930 %
00931 */
00932 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
00933 {
00934 #define Enhance(weight) \
00935   mean=((MagickRealType) r->red+pixel.red)/2; \
00936   distance=(MagickRealType) r->red-(MagickRealType) pixel.red; \
00937   distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
00938      mean)*distance*distance; \
00939   mean=((MagickRealType) r->green+pixel.green)/2; \
00940   distance=(MagickRealType) r->green-(MagickRealType) pixel.green; \
00941   distance_squared+=4.0*distance*distance; \
00942   mean=((MagickRealType) r->blue+pixel.blue)/2; \
00943   distance=(MagickRealType) r->blue-(MagickRealType) pixel.blue; \
00944   distance_squared+=QuantumScale*(3.0*((MagickRealType) \
00945     QuantumRange+1.0)-1.0-mean)*distance*distance; \
00946   mean=((MagickRealType) r->opacity+pixel.opacity)/2; \
00947   distance=(MagickRealType) r->opacity-(MagickRealType) pixel.opacity; \
00948   distance_squared+=QuantumScale*(3.0*((MagickRealType) \
00949     QuantumRange+1.0)-1.0-mean)*distance*distance; \
00950   if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
00951       QuantumRange/25.0f)) \
00952     { \
00953       aggregate.red+=(weight)*r->red; \
00954       aggregate.green+=(weight)*r->green; \
00955       aggregate.blue+=(weight)*r->blue; \
00956       aggregate.opacity+=(weight)*r->opacity; \
00957       total_weight+=(weight); \
00958     } \
00959   r++;
00960 #define EnhanceImageTag  "Enhance/Image"
00961 
00962   Image
00963     *enhance_image;
00964 
00965   long
00966     progress,
00967     y;
00968 
00969   MagickBooleanType
00970     status;
00971 
00972   MagickPixelPacket
00973     zero;
00974 
00975   ViewInfo
00976     **enhance_view,
00977     **image_view;
00978 
00979   /*
00980     Initialize enhanced image attributes.
00981   */
00982   assert(image != (const Image *) NULL);
00983   assert(image->signature == MagickSignature);
00984   if (image->debug != MagickFalse)
00985     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00986   assert(exception != (ExceptionInfo *) NULL);
00987   assert(exception->signature == MagickSignature);
00988   if ((image->columns < 5) || (image->rows < 5))
00989     return((Image *) NULL);
00990   enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00991     exception);
00992   if (enhance_image == (Image *) NULL)
00993     return((Image *) NULL);
00994   if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
00995     {
00996       InheritException(exception,&enhance_image->exception);
00997       enhance_image=DestroyImage(enhance_image);
00998       return((Image *) NULL);
00999     }
01000   /*
01001     Enhance image.
01002   */
01003   status=MagickTrue;
01004   progress=0;
01005   (void) ResetMagickMemory(&zero,0,sizeof(zero));
01006   image_view=AcquireCacheViewThreadSet(image);
01007   enhance_view=AcquireCacheViewThreadSet(enhance_image);
01008 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01009   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01010 #endif
01011   for (y=0; y < (lo