effect.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                   EEEEE  FFFFF  FFFFF  EEEEE  CCCC  TTTTT                   %
00007 %                   E      F      F      E     C        T                     %
00008 %                   EEE    FFF    FFF    EEE   C        T                     %
00009 %                   E      F      F      E     C        T                     %
00010 %                   EEEEE  F      F      EEEEE  CCCC    T                     %
00011 %                                                                             %
00012 %                                                                             %
00013 %                       MagickCore Image Effects Methods                      %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                 October 1996                                %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2009 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/property.h"
00045 #include "magick/blob.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/colorspace.h"
00050 #include "magick/constitute.h"
00051 #include "magick/decorate.h"
00052 #include "magick/draw.h"
00053 #include "magick/enhance.h"
00054 #include "magick/exception.h"
00055 #include "magick/exception-private.h"
00056 #include "magick/effect.h"
00057 #include "magick/fx.h"
00058 #include "magick/gem.h"
00059 #include "magick/geometry.h"
00060 #include "magick/image-private.h"
00061 #include "magick/list.h"
00062 #include "magick/log.h"
00063 #include "magick/memory_.h"
00064 #include "magick/monitor.h"
00065 #include "magick/monitor-private.h"
00066 #include "magick/montage.h"
00067 #include "magick/paint.h"
00068 #include "magick/pixel-private.h"
00069 #include "magick/property.h"
00070 #include "magick/quantize.h"
00071 #include "magick/quantum.h"
00072 #include "magick/random_.h"
00073 #include "magick/random-private.h"
00074 #include "magick/resample.h"
00075 #include "magick/resample-private.h"
00076 #include "magick/resize.h"
00077 #include "magick/resource_.h"
00078 #include "magick/segment.h"
00079 #include "magick/shear.h"
00080 #include "magick/signature-private.h"
00081 #include "magick/string_.h"
00082 #include "magick/thread-private.h"
00083 #include "magick/transform.h"
00084 #include "magick/threshold.h"
00085 
00086 /*
00087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00088 %                                                                             %
00089 %                                                                             %
00090 %                                                                             %
00091 %     A d a p t i v e B l u r I m a g e                                       %
00092 %                                                                             %
00093 %                                                                             %
00094 %                                                                             %
00095 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00096 %
00097 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
00098 %  intensely near image edges and more intensely far from edges.  We blur the
00099 %  image with a Gaussian operator of the given radius and standard deviation
00100 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00101 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
00102 %
00103 %  The format of the AdaptiveBlurImage method is:
00104 %
00105 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
00106 %        const double sigma,ExceptionInfo *exception)
00107 %      Image *AdaptiveBlurImageChannel(const Image *image,
00108 %        const ChannelType channel,double radius,const double sigma,
00109 %        ExceptionInfo *exception)
00110 %
00111 %  A description of each parameter follows:
00112 %
00113 %    o image: the image.
00114 %
00115 %    o channel: the channel type.
00116 %
00117 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00118 %      pixel.
00119 %
00120 %    o sigma: the standard deviation of the Laplacian, in pixels.
00121 %
00122 %    o exception: return any errors or warnings in this structure.
00123 %
00124 */
00125 
00126 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
00127   const double sigma,ExceptionInfo *exception)
00128 {
00129   Image
00130     *blur_image;
00131 
00132   blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
00133     exception);
00134   return(blur_image);
00135 }
00136 
00137 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
00138   const ChannelType channel,const double radius,const double sigma,
00139   ExceptionInfo *exception)
00140 {
00141 #define AdaptiveBlurImageTag  "Convolve/Image"
00142 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00143 
00144   double
00145     **kernel;
00146 
00147   Image
00148     *blur_image,
00149     *edge_image,
00150     *gaussian_image;
00151 
00152   long
00153     j,
00154     progress,
00155     y;
00156 
00157   MagickBooleanType
00158     status;
00159 
00160   MagickPixelPacket
00161     bias;
00162 
00163   MagickRealType
00164     alpha,
00165     normalize;
00166 
00167   register long
00168     i,
00169     u,
00170     v;
00171 
00172   unsigned long
00173     width;
00174 
00175   CacheView
00176     *blur_view,
00177     *edge_view,
00178     *image_view;
00179 
00180   assert(image != (const Image *) NULL);
00181   assert(image->signature == MagickSignature);
00182   if (image->debug != MagickFalse)
00183     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00184   assert(exception != (ExceptionInfo *) NULL);
00185   assert(exception->signature == MagickSignature);
00186   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00187   if (blur_image == (Image *) NULL)
00188     return((Image *) NULL);
00189   if (fabs(sigma) <= MagickEpsilon)
00190     return(blur_image);
00191   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00192     {
00193       InheritException(exception,&blur_image->exception);
00194       blur_image=DestroyImage(blur_image);
00195       return((Image *) NULL);
00196     }
00197   /*
00198     Edge detect the image brighness channel, level, blur, and level again.
00199   */
00200   edge_image=EdgeImage(image,radius,exception);
00201   if (edge_image == (Image *) NULL)
00202     {
00203       blur_image=DestroyImage(blur_image);
00204       return((Image *) NULL);
00205     }
00206   (void) LevelImage(edge_image,"20%,95%");
00207   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00208   if (gaussian_image != (Image *) NULL)
00209     {
00210       edge_image=DestroyImage(edge_image);
00211       edge_image=gaussian_image;
00212     }
00213   (void) LevelImage(edge_image,"10%,95%");
00214   /*
00215     Create a set of kernels from maximum (radius,sigma) to minimum.
00216   */
00217   width=GetOptimalKernelWidth2D(radius,sigma);
00218   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00219   if (kernel == (double **) NULL)
00220     {
00221       edge_image=DestroyImage(edge_image);
00222       blur_image=DestroyImage(blur_image);
00223       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00224     }
00225   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00226   for (i=0; i < (long) width; i+=2)
00227   {
00228     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00229       sizeof(**kernel));
00230     if (kernel[i] == (double *) NULL)
00231       break;
00232     j=0;
00233     for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00234     {
00235       for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00236       {
00237         alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00238         kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00239         j++;
00240       }
00241     }
00242     normalize=0.0;
00243     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00244       normalize+=kernel[i][j];
00245     if (fabs(normalize) <= MagickEpsilon)
00246       normalize=1.0;
00247     normalize=1.0/normalize;
00248     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00249       kernel[i][j]=(double) (normalize*kernel[i][j]);
00250   }
00251   if (i < (long) width)
00252     {
00253       for (i-=2; i >= 0; i-=2)
00254         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00255       kernel=(double **) RelinquishMagickMemory(kernel);
00256       edge_image=DestroyImage(edge_image);
00257       blur_image=DestroyImage(blur_image);
00258       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00259     }
00260   /*
00261     Adaptively blur image.
00262   */
00263   status=MagickTrue;
00264   progress=0;
00265   GetMagickPixelPacket(image,&bias);
00266   SetMagickPixelPacketBias(image,&bias);
00267   image_view=AcquireCacheView(image);
00268   edge_view=AcquireCacheView(edge_image);
00269   blur_view=AcquireCacheView(blur_image);
00270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00271   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00272 #endif
00273   for (y=0; y < (long) blur_image->rows; y++)
00274   {
00275     register const IndexPacket
00276       *__restrict indexes;
00277 
00278     register const PixelPacket
00279       *__restrict p,
00280       *__restrict r;
00281 
00282     register IndexPacket
00283       *__restrict blur_indexes;
00284 
00285     register long
00286       x;
00287 
00288     register PixelPacket
00289       *__restrict q;
00290 
00291     if (status == MagickFalse)
00292       continue;
00293     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00294     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00295       exception);
00296     if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00297       {
00298         status=MagickFalse;
00299         continue;
00300       }
00301     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
00302     for (x=0; x < (long) blur_image->columns; x++)
00303     {
00304       MagickPixelPacket
00305         pixel;
00306 
00307       MagickRealType
00308         alpha,
00309         gamma;
00310 
00311       register const double
00312         *__restrict k;
00313 
00314       register long
00315         i,
00316         u,
00317         v;
00318 
00319       gamma=0.0;
00320       i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
00321       if (i < 0)
00322         i=0;
00323       else
00324         if (i > (long) width)
00325           i=(long) width;
00326       if ((i & 0x01) != 0)
00327         i--;
00328       p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
00329         ((width-i)/2L),width-i,width-i,exception);
00330       if (p == (const PixelPacket *) NULL)
00331         break;
00332       indexes=GetCacheViewVirtualIndexQueue(image_view);
00333       pixel=bias;
00334       k=kernel[i];
00335       for (v=0; v < (long) (width-i); v++)
00336       {
00337         for (u=0; u < (long) (width-i); u++)
00338         {
00339           alpha=1.0;
00340           if (((channel & OpacityChannel) != 0) &&
00341               (image->matte != MagickFalse))
00342             alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00343           if ((channel & RedChannel) != 0)
00344             pixel.red+=(*k)*alpha*p->red;
00345           if ((channel & GreenChannel) != 0)
00346             pixel.green+=(*k)*alpha*p->green;
00347           if ((channel & BlueChannel) != 0)
00348             pixel.blue+=(*k)*alpha*p->blue;
00349           if ((channel & OpacityChannel) != 0)
00350             pixel.opacity+=(*k)*p->opacity;
00351           if (((channel & IndexChannel) != 0) &&
00352               (image->colorspace == CMYKColorspace))
00353             pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00354           gamma+=(*k)*alpha;
00355           k++;
00356           p++;
00357         }
00358       }
00359       gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00360       if ((channel & RedChannel) != 0)
00361         q->red=RoundToQuantum(gamma*pixel.red);
00362       if ((channel & GreenChannel) != 0)
00363         q->green=RoundToQuantum(gamma*pixel.green);
00364       if ((channel & BlueChannel) != 0)
00365         q->blue=RoundToQuantum(gamma*pixel.blue);
00366       if ((channel & OpacityChannel) != 0)
00367         q->opacity=RoundToQuantum(pixel.opacity);
00368       if (((channel & IndexChannel) != 0) &&
00369           (image->colorspace == CMYKColorspace))
00370         blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
00371       q++;
00372       r++;
00373     }
00374     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
00375       status=MagickFalse;
00376     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00377       {
00378         MagickBooleanType
00379           proceed;
00380 
00381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00382   #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
00383 #endif
00384         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
00385           image->rows);
00386         if (proceed == MagickFalse)
00387           status=MagickFalse;
00388       }
00389   }
00390   blur_image->type=image->type;
00391   blur_view=DestroyCacheView(blur_view);
00392   edge_view=DestroyCacheView(edge_view);
00393   image_view=DestroyCacheView(image_view);
00394   edge_image=DestroyImage(edge_image);
00395   for (i=0; i < (long) width;  i+=2)
00396     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00397   kernel=(double **) RelinquishMagickMemory(kernel);
00398   if (status == MagickFalse)
00399     blur_image=DestroyImage(blur_image);
00400   return(blur_image);
00401 }
00402 
00403 /*
00404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00405 %                                                                             %
00406 %                                                                             %
00407 %                                                                             %
00408 %     A d a p t i v e S h a r p e n I m a g e                                 %
00409 %                                                                             %
00410 %                                                                             %
00411 %                                                                             %
00412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00413 %
00414 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
00415 %  intensely near image edges and less intensely far from edges. We sharpen the
00416 %  image with a Gaussian operator of the given radius and standard deviation
00417 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00418 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
00419 %
00420 %  The format of the AdaptiveSharpenImage method is:
00421 %
00422 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
00423 %        const double sigma,ExceptionInfo *exception)
00424 %      Image *AdaptiveSharpenImageChannel(const Image *image,
00425 %        const ChannelType channel,double radius,const double sigma,
00426 %        ExceptionInfo *exception)
00427 %
00428 %  A description of each parameter follows:
00429 %
00430 %    o image: the image.
00431 %
00432 %    o channel: the channel type.
00433 %
00434 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00435 %      pixel.
00436 %
00437 %    o sigma: the standard deviation of the Laplacian, in pixels.
00438 %
00439 %    o exception: return any errors or warnings in this structure.
00440 %
00441 */
00442 
00443 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
00444   const double sigma,ExceptionInfo *exception)
00445 {
00446   Image
00447     *sharp_image;
00448 
00449   sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
00450     exception);
00451   return(sharp_image);
00452 }
00453 
00454 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
00455   const ChannelType channel,const double radius,const double sigma,
00456   ExceptionInfo *exception)
00457 {
00458 #define AdaptiveSharpenImageTag  "Convolve/Image"
00459 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00460 
00461   double
00462     **kernel;
00463 
00464   Image
00465     *sharp_image,
00466     *edge_image,
00467     *gaussian_image;
00468 
00469   long
00470     j,
00471     progress,
00472     y;
00473 
00474   MagickBooleanType
00475     status;
00476 
00477   MagickPixelPacket
00478     bias;
00479 
00480   MagickRealType
00481     alpha,
00482     normalize;
00483 
00484   register long
00485     i,
00486     u,
00487     v;
00488 
00489   unsigned long
00490     width;
00491 
00492   CacheView
00493     *sharp_view,
00494     *edge_view,
00495     *image_view;
00496 
00497   assert(image != (const Image *) NULL);
00498   assert(image->signature == MagickSignature);
00499   if (image->debug != MagickFalse)
00500     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00501   assert(exception != (ExceptionInfo *) NULL);
00502   assert(exception->signature == MagickSignature);
00503   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
00504   if (sharp_image == (Image *) NULL)
00505     return((Image *) NULL);
00506   if (fabs(sigma) <= MagickEpsilon)
00507     return(sharp_image);
00508   if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
00509     {
00510       InheritException(exception,&sharp_image->exception);
00511       sharp_image=DestroyImage(sharp_image);
00512       return((Image *) NULL);
00513     }
00514   /*
00515     Edge detect the image brighness channel, level, sharp, and level again.
00516   */
00517   edge_image=EdgeImage(image,radius,exception);
00518   if (edge_image == (Image *) NULL)
00519     {
00520       sharp_image=DestroyImage(sharp_image);
00521       return((Image *) NULL);
00522     }
00523   (void) LevelImage(edge_image,"20%,95%");
00524   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00525   if (gaussian_image != (Image *) NULL)
00526     {
00527       edge_image=DestroyImage(edge_image);
00528       edge_image=gaussian_image;
00529     }
00530   (void) LevelImage(edge_image,"10%,95%");
00531   /*
00532     Create a set of kernels from maximum (radius,sigma) to minimum.
00533   */
00534   width=GetOptimalKernelWidth2D(radius,sigma);
00535   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00536   if (kernel == (double **) NULL)
00537     {
00538       edge_image=DestroyImage(edge_image);
00539       sharp_image=DestroyImage(sharp_image);
00540       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00541     }
00542   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00543   for (i=0; i < (long) width; i+=2)
00544   {
00545     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00546       sizeof(**kernel));
00547     if (kernel[i] == (double *) NULL)
00548       break;
00549     j=0;
00550     for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00551     {
00552       for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00553       {
00554         alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00555         kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00556         j++;
00557       }
00558     }
00559     normalize=0.0;
00560     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00561       normalize+=kernel[i][j];
00562     if (fabs(normalize) <= MagickEpsilon)
00563       normalize=1.0;
00564     normalize=1.0/normalize;
00565     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00566       kernel[i][j]=(double) (normalize*kernel[i][j]);
00567   }
00568   if (i < (long) width)
00569     {
00570       for (i-=2; i >= 0; i-=2)
00571         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00572       kernel=(double **) RelinquishMagickMemory(kernel);
00573       edge_image=DestroyImage(edge_image);
00574       sharp_image=DestroyImage(sharp_image);
00575       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00576     }
00577   /*
00578     Adaptively sharpen image.
00579   */
00580   status=MagickTrue;
00581   progress=0;
00582   GetMagickPixelPacket(image,&bias);
00583   SetMagickPixelPacketBias(image,&bias);
00584   image_view=AcquireCacheView(image);
00585   edge_view=AcquireCacheView(edge_image);
00586   sharp_view=AcquireCacheView(sharp_image);
00587 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00588   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00589 #endif
00590   for (y=0; y < (long) sharp_image->rows; y++)
00591   {
00592     register const IndexPacket
00593       *__restrict indexes;
00594 
00595     register const PixelPacket
00596       *__restrict p,
00597       *__restrict r;
00598 
00599     register IndexPacket
00600       *__restrict sharp_indexes;
00601 
00602     register long
00603       x;
00604 
00605     register PixelPacket
00606       *__restrict q;
00607 
00608     if (status == MagickFalse)
00609       continue;
00610     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00611     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
00612       exception);
00613     if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00614       {
00615         status=MagickFalse;
00616         continue;
00617       }
00618     sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
00619     for (x=0; x < (long) sharp_image->columns; x++)
00620     {
00621       MagickPixelPacket
00622         pixel;
00623 
00624       MagickRealType
00625         alpha,
00626         gamma;
00627 
00628       register const double
00629         *__restrict k;
00630 
00631       register long
00632         i,
00633         u,
00634         v;
00635 
00636       gamma=0.0;
00637       i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
00638       if (i < 0)
00639         i=0;
00640       else
00641         if (i > (long) width)
00642           i=(long) width;
00643       if ((i & 0x01) != 0)
00644         i--;
00645       p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
00646         ((width-i)/2L),width-i,width-i,exception);
00647       if (p == (const PixelPacket *) NULL)
00648         break;
00649       indexes=GetCacheViewVirtualIndexQueue(image_view);
00650       k=kernel[i];
00651       pixel=bias;
00652       for (v=0; v < (long) (width-i); v++)
00653       {
00654         for (u=0; u < (long) (width-i); u++)
00655         {
00656           alpha=1.0;
00657           if (((channel & OpacityChannel) != 0) &&
00658               (image->matte != MagickFalse))
00659             alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00660           if ((channel & RedChannel) != 0)
00661             pixel.red+=(*k)*alpha*p->red;
00662           if ((channel & GreenChannel) != 0)
00663             pixel.green+=(*k)*alpha*p->green;
00664           if ((channel & BlueChannel) != 0)
00665             pixel.blue+=(*k)*alpha*p->blue;
00666           if ((channel & OpacityChannel) != 0)
00667             pixel.opacity+=(*k)*p->opacity;
00668           if (((channel & IndexChannel) != 0) &&
00669               (image->colorspace == CMYKColorspace))
00670             pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00671           gamma+=(*k)*alpha;
00672           k++;
00673           p++;
00674         }
00675       }
00676       gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00677       if ((channel & RedChannel) != 0)
00678         q->red=RoundToQuantum(gamma*pixel.red);
00679       if ((channel & GreenChannel) != 0)
00680         q->green=RoundToQuantum(gamma*pixel.green);
00681       if ((channel & BlueChannel) != 0)
00682         q->blue=RoundToQuantum(gamma*pixel.blue);
00683       if ((channel & OpacityChannel) != 0)
00684         q->opacity=RoundToQuantum(pixel.opacity);
00685       if (((channel & IndexChannel) != 0) &&
00686           (image->colorspace == CMYKColorspace))
00687         sharp_indexes[x]=RoundToQuantum(gamma*pixel.index);
00688       q++;
00689       r++;
00690     }
00691     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
00692       status=MagickFalse;
00693     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00694       {
00695         MagickBooleanType
00696           proceed;
00697 
00698 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00699   #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
00700 #endif
00701         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
00702           image->rows);
00703         if (proceed == MagickFalse)
00704           status=MagickFalse;
00705       }
00706   }
00707   sharp_image->type=image->type;
00708   sharp_view=DestroyCacheView(sharp_view);
00709   edge_view=DestroyCacheView(edge_view);
00710   image_view=DestroyCacheView(image_view);
00711   edge_image=DestroyImage(edge_image);
00712   for (i=0; i < (long) width;  i+=2)
00713     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00714   kernel=(double **) RelinquishMagickMemory(kernel);
00715   if (status == MagickFalse)
00716     sharp_image=DestroyImage(sharp_image);
00717   return(sharp_image);
00718 }
00719 
00720 /*
00721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00722 %                                                                             %
00723 %                                                                             %
00724 %                                                                             %
00725 %     B l u r I m a g e                                                       %
00726 %                                                                             %
00727 %                                                                             %
00728 %                                                                             %
00729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00730 %
00731 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
00732 %  of the given radius and standard deviation (sigma).  For reasonable results,
00733 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
00734 %  selects a suitable radius for you.
00735 %
00736 %  BlurImage() differs from GaussianBlurImage() in that it uses a separable
00737 %  kernel which is faster but mathematically equivalent to the non-separable
00738 %  kernel.
00739 %
00740 %  The format of the BlurImage method is:
00741 %
00742 %      Image *BlurImage(const Image *image,const double radius,
00743 %        const double sigma,ExceptionInfo *exception)
00744 %      Image *BlurImageChannel(const Image *image,const ChannelType channel,
00745 %        const double radius,const double sigma,ExceptionInfo *exception)
00746 %
00747 %  A description of each parameter follows:
00748 %
00749 %    o image: the image.
00750 %
00751 %    o channel: the channel type.
00752 %
00753 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00754 %      pixel.
00755 %
00756 %    o sigma: the standard deviation of the Gaussian, in pixels.
00757 %
00758 %    o exception: return any errors or warnings in this structure.
00759 %
00760 */
00761 
00762 MagickExport Image *BlurImage(const Image *image,const double radius,
00763   const double sigma,ExceptionInfo *exception)
00764 {
00765   Image
00766     *blur_image;
00767 
00768   blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
00769   return(blur_image);
00770 }
00771 
00772 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
00773 {
00774 #define KernelRank 3
00775 
00776   double
00777     *kernel;
00778 
00779   long
00780     bias;
00781 
00782   MagickRealType
00783     alpha,
00784     normalize;
00785 
00786   register long
00787     i;
00788 
00789   /*
00790     Generate a 1-D convolution kernel.
00791   */
00792   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00793   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00794   if (kernel == (double *) NULL)
00795     return(0);
00796   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00797   bias=KernelRank*(long) width/2;
00798   for (i=(-bias); i <= bias; i++)
00799   {
00800     alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
00801       MagickSigma*MagickSigma)));
00802     kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
00803   }
00804   normalize=0.0;
00805   for (i=0; i < (long) width; i++)
00806     normalize+=kernel[i];
00807   for (i=0; i < (long) width; i++)
00808     kernel[i]/=normalize;
00809   return(kernel);
00810 }
00811 
00812 MagickExport Image *BlurImageChannel(const Image *image,
00813   const ChannelType channel,const double radius,const double sigma,
00814   ExceptionInfo *exception)
00815 {
00816 #define BlurImageTag  "Blur/Image"
00817 
00818   double
00819     *kernel;
00820 
00821   Image
00822     *blur_image;
00823 
00824   long
00825     progress,
00826     x,
00827     y;
00828 
00829   MagickBooleanType
00830     status;
00831 
00832   MagickPixelPacket
00833     bias;
00834 
00835   register long
00836     i;
00837 
00838   unsigned long
00839     width;
00840 
00841   CacheView
00842     *blur_view,
00843     *image_view;
00844 
00845   /*
00846     Initialize blur image attributes.
00847   */
00848   assert(image != (Image *) NULL);
00849   assert(image->signature == MagickSignature);
00850   if (image->debug != MagickFalse)
00851     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00852   assert(exception != (ExceptionInfo *) NULL);
00853   assert(exception->signature == MagickSignature);
00854   blur_image=CloneImage(image,0,0,MagickTrue,exception);
00855   if (blur_image == (Image *) NULL)
00856     return((Image *) NULL);
00857   if (fabs(sigma) <= MagickEpsilon)
00858     return(blur_image);
00859   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00860     {
00861       InheritException(exception,&blur_image->exception);
00862       blur_image=DestroyImage(blur_image);
00863       return((Image *) NULL);
00864     }
00865   width=GetOptimalKernelWidth1D(radius,sigma);
00866   kernel=GetBlurKernel(width,sigma);
00867   if (kernel == (double *) NULL)
00868     {
00869       blur_image=DestroyImage(blur_image);
00870       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00871     }
00872   if (image->debug != MagickFalse)
00873     {
00874       char
00875         format[MaxTextExtent],
00876         *message;
00877 
00878       register const double
00879         *k;
00880 
00881       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00882         "  BlurImage with %ld kernel:",width);
00883       message=AcquireString("");
00884       k=kernel;
00885       for (i=0; i < (long) width; i++)
00886       {
00887         *message='\0';
00888         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
00889         (void) ConcatenateString(&message,format);
00890         (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
00891         (void) ConcatenateString(&message,format);
00892         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00893       }
00894       message=DestroyString(message);
00895     }
00896   /*
00897     Blur rows.
00898   */
00899   status=MagickTrue;
00900   progress=0;
00901   GetMagickPixelPacket(image,&bias);
00902   SetMagickPixelPacketBias(image,&bias);
00903   image_view=AcquireCacheView(image);
00904   blur_view=AcquireCacheView(blur_image);
00905 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00906   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00907 #endif
00908   for (y=0; y < (long) blur_image->rows; y++)
00909   {
00910     register const IndexPacket
00911       *__restrict indexes;
00912 
00913     register const PixelPacket
00914       *__restrict p;
00915 
00916     register IndexPacket
00917       *__restrict blur_indexes;
00918 
00919     register long
00920       x;
00921 
00922     register PixelPacket
00923       *__restrict q;
00924 
00925     if (status == MagickFalse)
00926       continue;
00927     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
00928       width,1,exception);
00929     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00930       exception);
00931     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00932       {
00933         status=MagickFalse;
00934         continue;
00935       }
00936     indexes=GetCacheViewVirtualIndexQueue(image_view);
00937     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
00938     for (x=0; x < (long) blur_image->columns; x++)
00939     {
00940       MagickPixelPacket
00941         pixel;
00942 
00943       register const double
00944         *__restrict k;
00945 
00946       register const PixelPacket
00947         *__restrict kernel_pixels;
00948 
00949       register long
00950         i;
00951 
00952       pixel=bias;
00953       k=kernel;
00954       kernel_pixels=p;
00955       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00956         {
00957           for (i=0; i < (long) width; i++)
00958           {
00959             pixel.red+=(*k)*kernel_pixels->red;
00960             pixel.green+=(*k)*kernel_pixels->green;
00961             pixel.blue+=(*k)*kernel_pixels->blue;
00962             k++;
00963             kernel_pixels++;
00964           }
00965           if ((channel & RedChannel) != 0)
00966             q->red=RoundToQuantum(pixel.red);
00967           if ((channel & GreenChannel) != 0)
00968             q->green=RoundToQuantum(pixel.green);
00969           if ((channel & BlueChannel) != 0)
00970             q->blue=RoundToQuantum(pixel.blue);
00971           if ((channel & OpacityChannel) != 0)
00972             {
00973               k=kernel;
00974               kernel_pixels=p;
00975               for (i=0; i < (long) width; i++)
00976               {
00977                 pixel.opacity+=(*k)*kernel_pixels->opacity;
00978                 k++;
00979                 kernel_pixels++;
00980               }
00981               q->opacity=RoundToQuantum(pixel.opacity);
00982             }
00983           if (((channel & IndexChannel) != 0) &&
00984               (image->colorspace == CMYKColorspace))
00985             {
00986               register const IndexPacket
00987                 *__restrict kernel_indexes;
00988 
00989               k=kernel;
00990               kernel_indexes=indexes;
00991               for (i=0; i < (long) width; i++)
00992               {
00993                 pixel.index+=(*k)*(*kernel_indexes);
00994                 k++;
00995                 kernel_indexes++;
00996               }
00997               blur_indexes[x]=RoundToQuantum(pixel.index);
00998             }
00999         }
01000       else
01001         {
01002           MagickRealType
01003             alpha,
01004             gamma;
01005 
01006           gamma=0.0;
01007           for (i=0; i < (long) width; i++)
01008           {
01009             alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01010               kernel_pixels->opacity));
01011             pixel.red+=(*k)*alpha*kernel_pixels->red;
01012             pixel.green+=(*k)*alpha*kernel_pixels->green;
01013             pixel.blue+=(*k)*alpha*kernel_pixels->blue;
01014             gamma+=(*k)*alpha;
01015             k++;
01016             kernel_pixels++;
01017           }
01018           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01019           if ((channel & RedChannel) != 0)
01020             q->red=RoundToQuantum(gamma*pixel.red);
01021           if ((channel & GreenChannel) != 0)
01022             q->green=RoundToQuantum(gamma*pixel.green);
01023           if ((channel & BlueChannel) != 0)
01024             q->blue=RoundToQuantum(gamma*pixel.blue);
01025           if ((channel & OpacityChannel) != 0)
01026             {
01027               k=kernel;
01028               kernel_pixels=p;
01029               for (i=0; i < (long) width; i++)
01030               {
01031                 pixel.opacity+=(*k)*kernel_pixels->opacity;
01032                 k++;
01033                 kernel_pixels++;
01034               }
01035               q->opacity=RoundToQuantum(pixel.opacity);
01036             }
01037           if (((channel & IndexChannel) != 0) &&
01038               (image->colorspace == CMYKColorspace))
01039             {
01040               register const IndexPacket
01041                 *__restrict kernel_indexes;
01042 
01043               k=kernel;
01044               kernel_pixels=p;
01045               kernel_indexes=indexes;
01046               for (i=0; i < (long) width; i++)
01047               {
01048                 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01049                   kernel_pixels->opacity));
01050                 pixel.index+=(*k)*alpha*(*kernel_indexes);
01051                 k++;
01052                 kernel_pixels++;
01053                 kernel_indexes++;
01054               }
01055               blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
01056             }
01057         }
01058       p++;
01059       q++;
01060     }
01061     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01062       status=MagickFalse;
01063     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01064       {
01065         MagickBooleanType
01066           proceed;
01067 
01068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01069   #pragma omp critical (MagickCore_BlurImageChannel)
01070 #endif
01071         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01072           blur_image->columns);
01073         if (proceed == MagickFalse)
01074           status=MagickFalse;
01075       }
01076   }
01077   blur_view=DestroyCacheView(blur_view);
01078   image_view=DestroyCacheView(image_view);
01079   /*
01080     Blur columns.
01081   */
01082   image_view=AcquireCacheView(blur_image);
01083   blur_view=AcquireCacheView(blur_image);
01084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01085   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01086 #endif
01087   for (x=0; x < (long) blur_image->columns; x++)
01088   {
01089     register const IndexPacket
01090       *__restrict indexes;
01091 
01092     register const PixelPacket
01093       *__restrict p;
01094 
01095     register IndexPacket
01096       *__restrict blur_indexes;
01097 
01098     register long
01099       y;
01100 
01101     register PixelPacket
01102       *__restrict q;
01103 
01104     if (status == MagickFalse)
01105       continue;
01106     p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
01107       width,exception);
01108     q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
01109     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01110       {
01111         status=MagickFalse;
01112         continue;
01113       }
01114     indexes=GetCacheViewVirtualIndexQueue(image_view);
01115     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
01116     for (y=0; y < (long) blur_image->rows; y++)
01117     {
01118       MagickPixelPacket
01119         pixel;
01120 
01121       register const double
01122         *__restrict k;
01123 
01124       register const PixelPacket
01125         *__restrict kernel_pixels;
01126 
01127       register long
01128         i;
01129 
01130       pixel=bias;
01131       k=kernel;
01132       kernel_pixels=p;
01133       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
01134         {
01135           for (i=0; i < (long) width; i++)
01136           {
01137             pixel.red+=(*k)*kernel_pixels->red;
01138             pixel.green+=(*k)*kernel_pixels->green;
01139             pixel.blue+=(*k)*kernel_pixels->blue;
01140             k++;
01141             kernel_pixels++;
01142           }
01143           if ((channel & RedChannel) != 0)
01144             q->red=RoundToQuantum(pixel.red);
01145           if ((channel & GreenChannel) != 0)
01146             q->green=RoundToQuantum(pixel.green);
01147           if ((channel & BlueChannel) != 0)
01148             q->blue=RoundToQuantum(pixel.blue);
01149           if ((channel & OpacityChannel) != 0)
01150             {
01151               k=kernel;
01152               kernel_pixels=p;
01153               for (i=0; i < (long) width; i++)
01154               {
01155                 pixel.opacity+=(*k)*kernel_pixels->opacity;
01156                 k++;
01157                 kernel_pixels++;
01158               }
01159               q->opacity=RoundToQuantum(pixel.opacity);
01160             }
01161           if (((channel & IndexChannel) != 0) &&
01162               (image->colorspace == CMYKColorspace))
01163             {
01164               register const IndexPacket
01165                 *__restrict kernel_indexes;
01166 
01167               k=kernel;
01168               kernel_indexes=indexes;
01169               for (i=0; i < (long) width; i++)
01170               {
01171                 pixel.index+=(*k)*(*kernel_indexes);
01172                 k++;
01173                 kernel_indexes++;
01174               }
01175               blur_indexes[y]=RoundToQuantum(pixel.index);
01176             }
01177         }
01178       else
01179         {
01180           MagickRealType
01181             alpha,
01182             gamma;
01183 
01184           gamma=0.0;
01185           for (i=0; i < (long) width; i++)
01186           {
01187             alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01188               kernel_pixels->opacity));
01189             pixel.red+=(*k)*alpha*kernel_pixels->red;
01190             pixel.green+=(*k)*alpha*kernel_pixels->green;
01191             pixel.blue+=(*k)*alpha*kernel_pixels->blue;
01192             gamma+=(*k)*alpha;
01193             k++;
01194             kernel_pixels++;
01195           }
01196           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01197           if ((channel & RedChannel) != 0)
01198             q->red=RoundToQuantum(gamma*pixel.red);
01199           if ((channel & GreenChannel) != 0)
01200             q->green=RoundToQuantum(gamma*pixel.green);
01201           if ((channel & BlueChannel) != 0)
01202             q->blue=RoundToQuantum(gamma*pixel.blue);
01203           if ((channel & OpacityChannel) != 0)
01204             {
01205               k=kernel;
01206               kernel_pixels=p;
01207               for (i=0; i < (long) width; i++)
01208               {
01209                 pixel.opacity+=(*k)*kernel_pixels->opacity;
01210                 k++;
01211                 kernel_pixels++;
01212               }
01213               q->opacity=RoundToQuantum(pixel.opacity);
01214             }
01215           if (((channel & IndexChannel) != 0) &&
01216               (image->colorspace == CMYKColorspace))
01217             {
01218               register const IndexPacket
01219                 *__restrict kernel_indexes;
01220 
01221               k=kernel;
01222               kernel_pixels=p;
01223               kernel_indexes=indexes;
01224               for (i=0; i < (long) width; i++)
01225               {
01226                 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01227                   kernel_pixels->opacity));
01228                 pixel.index+=(*k)*alpha*(*kernel_indexes);
01229                 k++;
01230                 kernel_pixels++;
01231                 kernel_indexes++;
01232               }
01233               blur_indexes[y]=RoundToQuantum(gamma*pixel.index);
01234             }
01235         }
01236       p++;
01237       q++;
01238     }
01239     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01240       status=MagickFalse;
01241     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01242       {
01243         MagickBooleanType
01244           proceed;
01245 
01246 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01247   #pragma omp critical (MagickCore_BlurImageChannel)
01248 #endif
01249         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01250           blur_image->columns);
01251         if (proceed == MagickFalse)
01252           status=MagickFalse;
01253       }
01254   }
01255   blur_view=DestroyCacheView(blur_view);
01256   image_view=DestroyCacheView(image_view);
01257   kernel=(double *) RelinquishMagickMemory(kernel);
01258   if (status == MagickFalse)
01259     blur_image=DestroyImage(blur_image);
01260   blur_image->type=image->type;
01261   return(blur_image);
01262 }
01263 
01264 /*
01265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01266 %                                                                             %
01267 %                                                                             %
01268 %                                                                             %
01269 %     D e s p e c k l e I m a g e                                             %
01270 %                                                                             %
01271 %                                                                             %
01272 %                                                                             %
01273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01274 %
01275 %  DespeckleImage() reduces the speckle noise in an image while perserving the
01276 %  edges of the original image.
01277 %
01278 %  The format of the DespeckleImage method is:
01279 %
01280 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
01281 %
01282 %  A description of each parameter follows:
01283 %
01284 %    o image: the image.
01285 %
01286 %    o exception: return any errors or warnings in this structure.
01287 %
01288 */
01289 
01290 static Quantum **DestroyPixelThreadSet(Quantum **pixels)
01291 {
01292   register long
01293     i;
01294 
01295   assert(pixels != (Quantum **) NULL);
01296   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01297     if (pixels[i] != (Quantum *) NULL)
01298       pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
01299   pixels=(Quantum **) RelinquishAlignedMemory(pixels);
01300   return(pixels);
01301 }
01302 
01303 static Quantum **AcquirePixelThreadSet(const size_t count)
01304 {
01305   register long
01306     i;
01307 
01308   Quantum
01309     **pixels;
01310 
01311   unsigned long
01312     number_threads;
01313 
01314   number_threads=GetOpenMPMaximumThreads();
01315   pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
01316   if (pixels == (Quantum **) NULL)
01317     return((Quantum **) NULL);
01318   (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
01319   for (i=0; i < (long) number_threads; i++)
01320   {
01321     pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
01322     if (pixels[i] == (Quantum *) NULL)
01323       return(DestroyPixelThreadSet(pixels));
01324   }
01325   return(pixels);
01326 }
01327 
01328 static void Hull(const long x_offset,const long y_offset,
01329   const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
01330   const int polarity)
01331 {
01332   long
01333     y;
01334 
01335   MagickRealType
01336     v;
01337 
01338   register long
01339     x;
01340 
01341   register Quantum
01342     *p,
01343     *q,
01344     *r,
01345     *s;
01346 
01347   assert(f != (Quantum *) NULL);
01348   assert(g != (Quantum *) NULL);
01349   p=f+(columns+2);
01350   q=g+(columns+2);
01351   r=p+(y_offset*((long) columns+2)+x_offset);
01352   for (y=0; y < (long) rows; y++)
01353   {
01354     p++;
01355     q++;
01356     r++;
01357     if (polarity > 0)
01358       for (x=(long) columns; x != 0; x--)
01359       {
01360         v=(MagickRealType) (*p);
01361         if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
01362           v+=ScaleCharToQuantum(1);
01363         *q=(Quantum) v;
01364         p++;
01365         q++;
01366         r++;
01367       }
01368     else
01369       for (x=(long) columns; x != 0; x--)
01370       {
01371         v=(MagickRealType) (*p);
01372         if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
01373           v-=(long) ScaleCharToQuantum(1);
01374         *q=(Quantum) v;
01375         p++;
01376         q++;
01377         r++;
01378       }
01379     p++;
01380     q++;
01381     r++;
01382   }
01383   p=f+(columns+2);
01384   q=g+(columns+2);
01385   r=q+(y_offset*((long) columns+2)+x_offset);
01386   s=q-(y_offset*((long) columns+2)+x_offset);
01387   for (y=0; y < (long) rows; y++)
01388   {
01389     p++;
01390     q++;
01391     r++;
01392     s++;
01393     if (polarity > 0)
01394       for (x=(long) columns; x != 0; x--)
01395       {
01396         v=(MagickRealType) (*q);
01397         if (((MagickRealType) *s >=
01398              (v+(MagickRealType) ScaleCharToQuantum(2))) &&
01399             ((MagickRealType) *r > v))
01400           v+=ScaleCharToQuantum(1);
01401         *p=(Quantum) v;
01402         p++;
01403         q++;
01404         r++;
01405         s++;
01406       }
01407     else
01408       for (x=(long) columns; x != 0; x--)
01409       {
01410         v=(MagickRealType) (*q);
01411         if (((MagickRealType) *s <=
01412              (v-(MagickRealType) ScaleCharToQuantum(2))) &&
01413             ((MagickRealType) *r < v))
01414           v-=(MagickRealType) ScaleCharToQuantum(1);
01415         *p=(Quantum) v;
01416         p++;
01417         q++;
01418         r++;
01419         s++;
01420       }
01421     p++;
01422     q++;
01423     r++;
01424     s++;
01425   }
01426 }
01427 
01428 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
01429 {
01430 #define DespeckleImageTag  "Despeckle/Image"
01431 
01432   CacheView
01433     *despeckle_view,
01434     *image_view;
01435 
01436   Image
01437     *despeckle_image;
01438 
01439   long
01440     channel;
01441 
01442   MagickBooleanType
01443     status;
01444 
01445   Quantum
01446     **buffers,
01447     **pixels;
01448 
01449   size_t
01450     length;
01451 
01452   static const int
01453     X[4] = {0, 1, 1,-1},
01454     Y[4] = {1, 0, 1, 1};
01455 
01456   /*
01457     Allocate despeckled image.
01458   */
01459   assert(image != (const Image *) NULL);
01460   assert(image->signature == MagickSignature);
01461   if (image->debug != MagickFalse)
01462     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01463   assert(exception != (ExceptionInfo *) NULL);
01464   assert(exception->signature == MagickSignature);
01465   despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01466     exception);
01467   if (despeckle_image == (Image *) NULL)
01468     return((Image *) NULL);
01469   if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
01470     {
01471       InheritException(exception,&despeckle_image->exception);
01472       despeckle_image=DestroyImage(despeckle_image);
01473       return((Image *) NULL);
01474     }
01475   /*
01476     Allocate image buffers.
01477   */
01478   length=(size_t) ((image->columns+2)*(image->rows+2));
01479   pixels=AcquirePixelThreadSet(length);
01480   buffers=AcquirePixelThreadSet(length);
01481   if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
01482     {
01483       if (buffers != (Quantum **) NULL)
01484         buffers=DestroyPixelThreadSet(buffers);
01485       if (pixels != (Quantum **) NULL)
01486         pixels=DestroyPixelThreadSet(pixels);
01487       despeckle_image=DestroyImage(despeckle_image);
01488       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01489     }
01490   /*
01491     Reduce speckle in the image.
01492   */
01493   status=MagickTrue;
01494   image_view=AcquireCacheView(image);
01495   despeckle_view=AcquireCacheView(despeckle_image);
01496 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01497   #pragma omp parallel for schedule(dynamic,4) shared(status)
01498 #endif
01499   for (channel=0; channel <= 3; channel++)
01500   {
01501     long
01502       j,
01503       y;
01504 
01505     register long
01506       i,
01507       id,
01508       x;
01509 
01510     register Quantum
01511       *buffer,
01512       *pixel;
01513 
01514     if (status == MagickFalse)
01515       continue;
01516     id=GetOpenMPThreadId();
01517     pixel=pixels[id];
01518     (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
01519     buffer=buffers[id];
01520     j=(long) image->columns+2;
01521     for (y=0; y < (long) image->rows; y++)
01522     {
01523       register const PixelPacket
01524         *__restrict p;
01525 
01526       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
01527       if (p == (const PixelPacket *) NULL)
01528         break;
01529       j++;
01530       for (x=0; x < (long) image->columns; x++)
01531       {
01532         switch (channel)
01533         {
01534           case 0: pixel[j]=p->red; break;
01535           case 1: pixel[j]=p->green; break;
01536           case 2: pixel[j]=p->blue; break;
01537           case 3: pixel[j]=p->opacity; break;
01538           default: break;
01539         }
01540         p++;
01541         j++;
01542       }
01543       j++;
01544     }
01545     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
01546     for (i=0; i < 4; i++)
01547     {
01548       Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
01549       Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
01550       Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
01551       Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
01552     }
01553     j=(long) image->columns+2;
01554     for (y=0; y < (long) image->rows; y++)
01555     {
01556       MagickBooleanType
01557         sync;
01558 
01559       register PixelPacket
01560         *__restrict q;
01561 
01562       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
01563         1,exception);
01564       if (q == (PixelPacket *) NULL)
01565         break;
01566       j++;
01567       for (x=0; x < (long) image->columns; x++)
01568       {
01569         switch (channel)
01570         {
01571           case 0: q->red=pixel[j]; break;
01572           case 1: q->green=pixel[j]; break;
01573           case 2: q->blue=pixel[j]; break;
01574           case 3: q->opacity=pixel[j]; break;
01575           default: break;
01576         }
01577         q++;
01578         j++;
01579       }
01580       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
01581       if (sync == MagickFalse)
01582         {
01583           status=MagickFalse;
01584           break;
01585         }
01586       j++;
01587     }
01588     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01589       {
01590         MagickBooleanType
01591           proceed;
01592 
01593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01594   #pragma omp critical (MagickCore_DespeckleImage)
01595 #endif
01596         proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
01597         if (proceed == MagickFalse)
01598           status=MagickFalse;
01599       }
01600   }
01601   despeckle_view=DestroyCacheView(despeckle_view);
01602   image_view=DestroyCacheView(image_view);
01603   buffers=DestroyPixelThreadSet(buffers);
01604   pixels=DestroyPixelThreadSet(pixels);
01605   despeckle_image->type=image->type;
01606   if (status == MagickFalse)
01607     despeckle_image=DestroyImage(despeckle_image);
01608   return(despeckle_image);
01609 }
01610 
01611 /*
01612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01613 %                                                                             %
01614 %                                                                             %
01615 %                                                                             %
01616 %     E d g e I m a g e                                                       %
01617 %                                                                             %
01618 %                                                                             %
01619 %                                                                             %
01620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01621 %
01622 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
01623 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
01624 %  radius for you.
01625 %
01626 %  The format of the EdgeImage method is:
01627 %
01628 %      Image *EdgeImage(const Image *image,const double radius,
01629 %        ExceptionInfo *exception)
01630 %
01631 %  A description of each parameter follows:
01632 %
01633 %    o image: the image.
01634 %
01635 %    o radius: the radius of the pixel neighborhood.
01636 %
01637 %    o exception: return any errors or warnings in this structure.
01638 %
01639 */
01640 MagickExport Image *EdgeImage(const Image *image,const double radius,
01641   ExceptionInfo *exception)
01642 {
01643   Image
01644     *edge_image;
01645 
01646   double
01647     *kernel;
01648 
01649   register long
01650     i;
01651 
01652   unsigned long
01653     width;
01654 
01655   assert(image != (const Image *) NULL);
01656   assert(image->signature == MagickSignature);
01657   if (image->debug != MagickFalse)
01658     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01659   assert(exception != (ExceptionInfo *) NULL);
01660   assert(exception->signature == MagickSignature);
01661   width=GetOptimalKernelWidth1D(radius,0.5);
01662   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01663   if (kernel == (double *) NULL)
01664     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01665   for (i=0; i < (long) (width*width); i++)
01666     kernel[i]=(-1.0);
01667   kernel[i/2]=(double) (width*width-1.0);
01668   edge_image=ConvolveImage(image,width,kernel,exception);
01669   kernel=(double *) RelinquishMagickMemory(kernel);
01670   return(edge_image);
01671 }
01672 
01673 /*
01674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01675 %                                                                             %
01676 %                                                                             %
01677 %                                                                             %
01678 %     E m b o s s I m a g e                                                   %
01679 %                                                                             %
01680 %                                                                             %
01681 %                                                                             %
01682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01683 %
01684 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
01685 %  We convolve the image with a Gaussian operator of the given radius and
01686 %  standard deviation (sigma).  For reasonable results, radius should be
01687 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
01688 %  radius for you.
01689 %
01690 %  The format of the EmbossImage method is:
01691 %
01692 %      Image *EmbossImage(const Image *image,const double radius,
01693 %        const double sigma,ExceptionInfo *exception)
01694 %
01695 %  A description of each parameter follows:
01696 %
01697 %    o image: the image.
01698 %
01699 %    o radius: the radius of the pixel neighborhood.
01700 %
01701 %    o sigma: the standard deviation of the Gaussian, in pixels.
01702 %
01703 %    o exception: return any errors or warnings in this structure.
01704 %
01705 */
01706 MagickExport Image *EmbossImage(const Image *image,const double radius,
01707   const double sigma,ExceptionInfo *exception)
01708 {
01709   double
01710     *kernel;
01711 
01712   Image
01713     *emboss_image;
01714 
01715   long
01716     j;
01717 
01718   MagickRealType
01719     alpha;
01720 
01721   register long
01722     i,
01723     u,
01724     v;
01725 
01726   unsigned long
01727     width;
01728 
01729   assert(image != (Image *) NULL);
01730   assert(image->signature == MagickSignature);
01731   if (image->debug != MagickFalse)
01732     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01733   assert(exception != (ExceptionInfo *) NULL);
01734   assert(exception->signature == MagickSignature);
01735   width=GetOptimalKernelWidth2D(radius,sigma);
01736   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01737   if (kernel == (double *) NULL)
01738     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01739   i=0;
01740   j=(long) width/2;
01741   for (v=(-((long) width/2)); v <= (long) (width/2); v++)
01742   {
01743     for (u=(-((long) width/2)); u <= (long) (width/2); u++)
01744     {
01745       alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
01746       kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
01747         (2.0*MagickPI*MagickSigma*MagickSigma));
01748       if (u != j)
01749         kernel[i]=0.0;
01750       i++;
01751     }
01752     j--;
01753   }
01754   emboss_image=ConvolveImage(image,width,kernel,exception);
01755   if (emboss_image != (Image *) NULL)
01756     (void) EqualizeImage(emboss_image);
01757   kernel=(double *) RelinquishMagickMemory(kernel);
01758   return(emboss_image);
01759 }
01760 
01761 /*
01762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01763 %                                                                             %
01764 %                                                                             %
01765 %                                                                             %
01766 %     G a u s s i a n B l u r I m a g e                                       %
01767 %                                                                             %
01768 %                                                                             %
01769 %                                                                             %
01770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01771 %
01772 %  GaussianBlurImage() blurs an image.  We convolve the image with a
01773 %  Gaussian operator of the given radius and standard deviation (sigma).
01774 %  For reasonable results, the radius should be larger than sigma.  Use a
01775 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
01776 %
01777 %  The format of the GaussianBlurImage method is:
01778 %
01779 %      Image *GaussianBlurImage(const Image *image,onst double radius,
01780 %        const double sigma,ExceptionInfo *exception)
01781 %      Image *GaussianBlurImageChannel(const Image *image,
01782 %        const ChannelType channel,const double radius,const double sigma,
01783 %        ExceptionInfo *exception)
01784 %
01785 %  A description of each parameter follows:
01786 %
01787 %    o image: the image.
01788 %
01789 %    o channel: the channel type.
01790 %
01791 %    o radius: the radius of the Gaussian, in pixels, not counting the center
01792 %      pixel.
01793 %
01794 %    o sigma: the standard deviation of the Gaussian, in pixels.
01795 %
01796 %    o exception: return any errors or warnings in this structure.
01797 %
01798 */
01799 
01800 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
01801   const double sigma,ExceptionInfo *exception)
01802 {
01803   Image
01804     *blur_image;
01805 
01806   blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
01807     exception);
01808   return(blur_image);
01809 }
01810 
01811 MagickExport Image *GaussianBlurImageChannel(const Image *image,
01812   const ChannelType channel,const double radius,const double sigma,
01813   ExceptionInfo *exception)
01814 {
01815   double
01816     *kernel;
01817 
01818   Image
01819     *blur_image;
01820 
01821   MagickRealType
01822     alpha;
01823 
01824   register long
01825     i,
01826     u,
01827     v;
01828 
01829   unsigned long
01830     width;
01831 
01832   assert(image != (const Image *) NULL);
01833   assert(image->signature == MagickSignature);
01834   if (image->debug != MagickFalse)
01835     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01836   assert(exception != (ExceptionInfo *) NULL);
01837   assert(exception->signature == MagickSignature);
01838   width=GetOptimalKernelWidth2D(radius,sigma);
01839   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
01840   if (kernel == (double *) NULL)
01841     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01842   i=0;
01843   for (v=(-((long) width/2)); v <= (long) (width/2); v++)
01844   {
01845     for (u=(-((long) width/2)); u <= (long) (width/2); u++)
01846     {
01847       alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
01848       kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
01849       i++;
01850     }
01851   }
01852   blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
01853   kernel=(double *) RelinquishMagickMemory(kernel);
01854   return(blur_image);
01855 }
01856 
01857 /*
01858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01859 %                                                                             %
01860 %                                                                             %
01861 %                                                                             %
01862 %     M e d i a n F i l t e r I m a g e                                       %
01863 %                                                                             %
01864 %                                                                             %
01865 %                                                                             %
01866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01867 %
01868 %  MedianFilterImage() applies a digital filter that improves the quality
01869 %  of a noisy image.  Each pixel is replaced by the median in a set of
01870 %  neighboring pixels as defined by radius.
01871 %
01872 %  The algorithm was contributed by Mike Edmonds and implements an insertion
01873 %  sort for selecting median color-channel values.  For more on this algorithm
01874 %  see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
01875 %  Pugh in the June 1990 of Communications of the ACM.
01876 %
01877 %  The format of the MedianFilterImage method is:
01878 %
01879 %      Image *MedianFilterImage(const Image *image,const double radius,
01880 %        ExceptionInfo *exception)
01881 %
01882 %  A description of each parameter follows:
01883 %
01884 %    o image: the image.
01885 %
01886 %    o radius: the radius of the pixel neighborhood.
01887 %
01888 %    o exception: return any errors or warnings in this structure.
01889 %
01890 */
01891 
01892 #define MedianListChannels  5
01893 
01894 typedef struct _MedianListNode
01895 {
01896   unsigned long
01897     next[9],
01898     count,
01899     signature;
01900 } MedianListNode;
01901 
01902 typedef struct _MedianSkipList
01903 {
01904   long
01905     level;
01906 
01907   MedianListNode
01908     *nodes;
01909 } MedianSkipList;
01910 
01911 typedef struct _MedianPixelList
01912 {
01913   unsigned long
01914     center,
01915     seed,
01916     signature;
01917 
01918   MedianSkipList
01919     lists[MedianListChannels];
01920 } MedianPixelList;
01921 
01922 static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
01923 {
01924   register long
01925     i;
01926 
01927   if (pixel_list == (MedianPixelList *) NULL)
01928     return((MedianPixelList *) NULL);
01929   for (i=0; i < MedianListChannels; i++)
01930     if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
01931       pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
01932         pixel_list->lists[i].nodes);
01933   pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
01934   return(pixel_list);
01935 }
01936 
01937 static MedianPixelList **DestroyMedianPixelListThreadSet(
01938   MedianPixelList **pixel_list)
01939 {
01940   register long
01941     i;
01942 
01943   assert(pixel_list != (MedianPixelList **) NULL);
01944   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01945     if (pixel_list[i] != (MedianPixelList *) NULL)
01946       pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
01947   pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
01948   return(pixel_list);
01949 }
01950 
01951 static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
01952 {
01953   MedianPixelList
01954     *pixel_list;
01955 
01956   register long
01957     i;
01958 
01959   pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
01960   if (pixel_list == (MedianPixelList *) NULL)
01961     return(pixel_list);
01962   (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
01963   pixel_list->center=width*width/2;
01964   for (i=0; i < MedianListChannels; i++)
01965   {
01966     pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
01967       sizeof(*pixel_list->lists[i].nodes));
01968     if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
01969       return(DestroyMedianPixelList(pixel_list));
01970     (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
01971       sizeof(*pixel_list->lists[i].nodes));
01972   }
01973   pixel_list->signature=MagickSignature;
01974   return(pixel_list);
01975 }
01976 
01977 static MedianPixelList **AcquireMedianPixelListThreadSet(
01978   const unsigned long width)
01979 {
01980   register long
01981     i;
01982 
01983   MedianPixelList
01984     **pixel_list;
01985 
01986   unsigned long
01987     number_threads;
01988 
01989   number_threads=GetOpenMPMaximumThreads();
01990   pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
01991     sizeof(*pixel_list));
01992   if (pixel_list == (MedianPixelList **) NULL)
01993     return((MedianPixelList **) NULL);
01994   (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
01995   for (i=0; i < (long) number_threads; i++)
01996   {
01997     pixel_list[i]=AcquireMedianPixelList(width);
01998     if (pixel_list[i] == (MedianPixelList *) NULL)
01999       return(DestroyMedianPixelListThreadSet(pixel_list));
02000   }
02001   return(pixel_list);
02002 }
02003 
02004 static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
02005   const long channel,const unsigned long color)
02006 {
02007   register long
02008     level;
02009 
02010   register MedianSkipList
02011     *list;
02012 
02013   unsigned long
02014     search,
02015     update[9];
02016 
02017   /*
02018     Initialize the node.
02019   */
02020   list=pixel_list->lists+channel;
02021   list->nodes[color].signature=pixel_list->signature;
02022   list->nodes[color].count=1;
02023   /*
02024     Determine where it belongs in the list.
02025   */
02026   search=65536UL;
02027   for (level=list->level; level >= 0; level--)
02028   {
02029     while (list->nodes[search].next[level] < color)
02030       search=list->nodes[search].next[level];
02031     update[level]=search;
02032   }
02033   /*
02034     Generate a pseudo-random level for this node.
02035   */
02036   for (level=0; ; level++)
02037   {
02038     pixel_list->seed=(pixel_list->seed*42893621L)+1L;
02039     if ((pixel_list->seed & 0x300) != 0x300)
02040       break;
02041   }
02042   if (level > 8)
02043     level=8;
02044   if (level > (list->level+2))
02045     level=list->level+2;
02046   /*
02047     If we're raising the list's level, link back to the root node.
02048   */
02049   while (level > list->level)
02050   {
02051     list->level++;
02052     update[list->level]=65536UL;
02053   }
02054   /*
02055     Link the node into the skip-list.
02056   */
02057   do
02058   {
02059     list->nodes[color].next[level]=list->nodes[update[level]].next[level];
02060     list->nodes[update[level]].next[level]=color;
02061   }
02062   while (level-- > 0);
02063 }
02064 
02065 static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
02066 {
02067   MagickPixelPacket
02068     pixel;
02069 
02070   register long
02071     channel;
02072 
02073   register MedianSkipList
02074     *list;
02075 
02076   unsigned long
02077     center,
02078     color,
02079     count;
02080 
02081   unsigned short
02082     channels[MedianListChannels];
02083 
02084   /*
02085     Find the median value for each of the color.
02086   */
02087   center=pixel_list->center;
02088   for (channel=0; channel < 5; channel++)
02089   {
02090     list=pixel_list->lists+channel;
02091     color=65536UL;
02092     count=0;
02093     do
02094     {
02095       color=list->nodes[color].next[0];
02096       count+=list->nodes[color].count;
02097     }
02098     while (count <= center);
02099     channels[channel]=(unsigned short) color;
02100   }
02101   GetMagickPixelPacket((const Image *) NULL,&pixel);
02102   pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
02103   pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
02104   pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
02105   pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
02106   pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
02107   return(pixel);
02108 }
02109 
02110 static inline void InsertMedianPixelList(const Image *image,
02111   const PixelPacket *pixel,const IndexPacket *indexes,
02112   MedianPixelList *pixel_list)
02113 {
02114   unsigned long
02115     signature;
02116 
02117   unsigned short
02118     index;
02119 
02120   index=ScaleQuantumToShort(pixel->red);
02121   signature=pixel_list->lists[0].nodes[index].signature;
02122   if (signature == pixel_list->signature)
02123     pixel_list->lists[0].nodes[index].count++;
02124   else
02125     AddNodeMedianPixelList(pixel_list,0,index);
02126   index=ScaleQuantumToShort(pixel->green);
02127   signature=pixel_list->lists[1].nodes[index].signature;
02128   if (signature == pixel_list->signature)
02129     pixel_list->lists[1].nodes[index].count++;
02130   else
02131     AddNodeMedianPixelList(pixel_list,1,index);
02132   index=ScaleQuantumToShort(pixel->blue);
02133   signature=pixel_list->lists[2].nodes[index].signature;
02134   if (signature == pixel_list->signature)
02135     pixel_list->lists[2].nodes[index].count++;
02136   else
02137     AddNodeMedianPixelList(pixel_list,2,index);
02138   index=ScaleQuantumToShort(pixel->opacity);
02139   signature=pixel_list->lists[3].nodes[index].signature;
02140   if (signature == pixel_list->signature)
02141     pixel_list->lists[3].nodes[index].count++;
02142   else
02143     AddNodeMedianPixelList(pixel_list,3,index);
02144   if (image->colorspace == CMYKColorspace)
02145     index=ScaleQuantumToShort(*indexes);
02146   signature=pixel_list->lists[4].nodes[index].signature;
02147   if (signature == pixel_list->signature)
02148     pixel_list->lists[4].nodes[index].count++;
02149   else
02150     AddNodeMedianPixelList(pixel_list,4,index);
02151 }
02152 
02153 static void ResetMedianPixelList(MedianPixelList *pixel_list)
02154 {
02155   int
02156     level;
02157 
02158   register long
02159     channel;
02160 
02161   register MedianListNode
02162     *root;
02163 
02164   register MedianSkipList
02165     *list;
02166 
02167   /*
02168     Reset the skip-list.
02169   */
02170   for (channel=0; channel < 5; channel++)
02171   {
02172     list=pixel_list->lists+channel;
02173     root=list->nodes+65536UL;
02174     list->level=0;
02175     for (level=0; level < 9; level++)
02176       root->next[level]=65536UL;
02177   }
02178   pixel_list->seed=pixel_list->signature++;
02179 }
02180 
02181 MagickExport Image *MedianFilterImage(const Image *image,const double radius,
02182   ExceptionInfo *exception)
02183 {
02184 #define MedianFilterImageTag  "MedianFilter/Image"
02185 
02186   Image
02187     *median_image;
02188 
02189   long
02190     progress,
02191     y;
02192 
02193   MagickBooleanType
02194     status;
02195 
02196   MedianPixelList
02197     **pixel_list;
02198 
02199   unsigned long
02200     width;
02201 
02202   CacheView
02203     *image_view,
02204     *median_view;
02205 
02206   /*
02207     Initialize median image attributes.
02208   */
02209   assert(image != (Image *) NULL);
02210   assert(image->signature == MagickSignature);
02211   if (image->debug != MagickFalse)
02212     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02213   assert(exception != (ExceptionInfo *) NULL);
02214   assert(exception->signature == MagickSignature);
02215   width=GetOptimalKernelWidth2D(radius,0.5);
02216   if ((image->columns < width) || (image->rows < width))
02217     ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
02218   median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
02219     exception);
02220   if (median_image == (Image *) NULL)
02221     return((Image *) NULL);
02222   if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
02223     {
02224       InheritException(exception,&median_image->exception);
02225       median_image=DestroyImage(median_image);
02226       return((Image *) NULL);
02227     }
02228   pixel_list=AcquireMedianPixelListThreadSet(width);
02229   if (pixel_list == (MedianPixelList **) NULL)
02230     {
02231       median_image=DestroyImage(median_image);
02232       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02233     }
02234   /*
02235     Median filter each image row.
02236   */
02237   status=MagickTrue;
02238   progress=0;
02239   image_view=AcquireCacheView(image);
02240   median_view=AcquireCacheView(median_image);
02241 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02242   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02243 #endif
02244   for (y=0; y < (long) median_image->rows; y++)
02245   {
02246     register const IndexPacket
02247       *__restrict indexes;
02248 
02249     register const PixelPacket
02250       *__restrict p;
02251 
02252     register IndexPacket
02253       *__restrict median_indexes;
02254 
02255     register long
02256       id,
02257       x;
02258 
02259     register PixelPacket
02260       *__restrict q;
02261 
02262     if (status == MagickFalse)
02263       continue;
02264     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
02265       2L),image->columns+width,width,exception);
02266     q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
02267       exception);
02268     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02269       {
02270         status=MagickFalse;
02271         continue;
02272       }
02273     indexes=GetCacheViewVirtualIndexQueue(image_view);
02274     median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
02275     id=GetOpenMPThreadId();
02276     for (x=0; x < (long) median_image->columns; x++)
02277     {
02278       MagickPixelPacket
02279         pixel;
02280 
02281       register const PixelPacket
02282         *__restrict r;
02283 
02284       register const IndexPacket
02285         *__restrict s;
02286 
02287       register long
02288         u,
02289         v;
02290 
02291       r=p;
02292       s=indexes+x;
02293       ResetMedianPixelList(pixel_list[id]);
02294       for (v=0; v < (long) width; v++)
02295       {
02296         for (u=0; u < (long) width; u++)
02297           InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
02298         r+=image->columns+width;
02299         s+=image->columns+width;
02300       }
02301       pixel=GetMedianPixelList(pixel_list[id]);
02302       SetPixelPacket(median_image,&pixel,q,median_indexes+x);
02303       p++;
02304       q++;
02305     }
02306     if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
02307       status=MagickFalse;
02308     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02309       {
02310         MagickBooleanType
02311           proceed;
02312 
02313 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02314   #pragma omp critical (MagickCore_MedianFilterImage)
02315 #endif
02316         proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
02317           image->rows);
02318         if (proceed == MagickFalse)
02319           status=MagickFalse;
02320       }
02321   }
02322   median_view=DestroyCacheView(median_view);
02323   image_view=DestroyCacheView(image_view);
02324   pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
02325   return(median_image);
02326 }
02327 
02328 /*
02329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02330 %                                                                             %
02331 %                                                                             %
02332 %                                                                             %
02333 %     M o t i o n B l u r I m a g e                                           %
02334 %                                                                             %
02335 %                                                                             %
02336 %                                                                             %
02337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02338 %
02339 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
02340 %  Gaussian operator of the given radius and standard deviation (sigma).
02341 %  For reasonable results, radius should be larger than sigma.  Use a
02342 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
02343 %  Angle gives the angle of the blurring motion.
02344 %
02345 %  Andrew Protano contributed this effect.
02346 %
02347 %  The format of the MotionBlurImage method is:
02348 %
02349 %    Image *MotionBlurImage(const Image *image,const double radius,
02350 %      const double sigma,const double angle,ExceptionInfo *exception)
02351 %    Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
02352 %      const double radius,const double sigma,const double angle,
02353 %      ExceptionInfo *exception)
02354 %
02355 %  A description of each parameter follows:
02356 %
02357 %    o image: the image.
02358 %
02359 %    o channel: the channel type.
02360 %
02361 %    o radius: the radius of the Gaussian, in pixels, not counting the center
02362 %    o radius: the radius of the Gaussian, in pixels, not counting
02363 %      the center pixel.
02364 %
02365 %    o sigma: the standard deviation of the Gaussian, in pixels.
02366 %
02367 %    o angle: Apply the effect along this angle.
02368 %
02369 %    o exception: return any errors or warnings in this structure.
02370 %
02371 */
02372 
02373 static double *GetMotionBlurKernel(unsigned long width,
02374   const MagickRealType sigma)
02375 {
02376 #define KernelRank 3
02377 
02378   double
02379     *kernel;
02380 
02381   long
02382     bias;
02383 
02384   MagickRealType
02385     alpha,
02386     normalize;
02387 
02388   register long
02389     i;
02390 
02391   /*
02392     Generate a 1-D convolution kernel.
02393   */
02394   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02395   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
02396   if (kernel == (double *) NULL)
02397     return(kernel);
02398   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
02399   bias=(long) (KernelRank*width);
02400   for (i=0; i < (long) bias; i++)
02401   {
02402     alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
02403       MagickSigma*MagickSigma)));
02404     kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
02405   }
02406   normalize=0.0;
02407   for (i=0; i < (long) width; i++)
02408     normalize+=kernel[i];
02409   for (i=0; i < (long) width; i++)
02410     kernel[i]/=normalize;
02411   return(kernel);
02412 }
02413 
02414 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
02415   const double sigma,const double angle,ExceptionInfo *exception)
02416 {
02417   Image
02418     *motion_blur;
02419 
02420   motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
02421     exception);
02422   return(motion_blur);
02423 }
02424 
02425 MagickExport Image *MotionBlurImageChannel(const Image *image,
02426   const ChannelType channel,const double radius,const double sigma,
02427   const double angle,ExceptionInfo *exception)
02428 {
02429   typedef struct _OffsetInfo
02430   {
02431     long
02432       x,
02433       y;
02434   } OffsetInfo;
02435 
02436   double
02437     *kernel;
02438 
02439   Image
02440     *blur_image;
02441 
02442   long
02443     progress,
02444     y;
02445 
02446   MagickBooleanType
02447     status;
02448 
02449   MagickPixelPacket
02450     bias;
02451 
02452   OffsetInfo
02453     *offset;
02454 
02455   PointInfo
02456     point;
02457 
02458   register long
02459     i;
02460 
02461   unsigned long
02462     width;
02463 
02464   CacheView
02465     *blur_view,
02466     *image_view;
02467 
02468   assert(image != (Image *) NULL);
02469   assert(image->signature == MagickSignature);
02470   if (image->debug != MagickFalse)
02471     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02472   assert(exception != (ExceptionInfo *) NULL);
02473   width=GetOptimalKernelWidth1D(radius,sigma);
02474   kernel=GetMotionBlurKernel(width,sigma);
02475   if (kernel == (double *) NULL)
02476     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02477   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
02478   if (offset == (OffsetInfo *) NULL)
02479     {
02480       kernel=(double *) RelinquishMagickMemory(kernel);
02481       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02482     }
02483   blur_image=CloneImage(image,0,0,MagickTrue,exception);
02484   if (blur_image == (Image *) NULL)
02485     {
02486       kernel=(double *) RelinquishMagickMemory(kernel);
02487       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02488       return((Image *) NULL);
02489     }
02490   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
02491     {
02492       kernel=(double *) RelinquishMagickMemory(kernel);
02493       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02494       InheritException(exception,&blur_image->exception);
02495       blur_image=DestroyImage(blur_image);
02496       return((Image *) NULL);
02497     }
02498   point.x=(double) width*sin(DegreesToRadians(angle));
02499   point.y=(double) width*cos(DegreesToRadians(angle));
02500   for (i=0; i < (long) width; i++)
02501   {
02502     offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
02503     offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
02504   }
02505   /*
02506     Motion blur image.
02507   */
02508   status=MagickTrue;
02509   progress=0;
02510   GetMagickPixelPacket(image,&bias);
02511   image_view=AcquireCacheView(image);
02512   blur_view=AcquireCacheView(blur_image);
02513 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02514   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02515 #endif
02516   for (y=0; y < (long) image->rows; y++)
02517   {
02518     register IndexPacket
02519       *__restrict blur_indexes;
02520 
02521     register long
02522       x;
02523 
02524     register PixelPacket
02525       *__restrict q;
02526 
02527     if (status == MagickFalse)
02528       continue;
02529     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
02530       exception);
02531     if (q == (PixelPacket *) NULL)
02532       {
02533         status=MagickFalse;
02534         continue;
02535       }
02536     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
02537     for (x=0; x < (long) image->columns; x++)
02538     {
02539       MagickPixelPacket
02540         qixel;
02541 
02542       PixelPacket
02543         pixel;
02544 
02545       register double
02546         *__restrict k;
02547 
02548       register long
02549         i;
02550 
02551       register const IndexPacket
02552         *__restrict indexes;
02553 
02554       k=kernel;
02555       qixel=bias;
02556       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
02557         {
02558           for (i=0; i < (long) width; i++)
02559           {
02560             (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
02561               offset[i].y,&pixel,exception);
02562             qixel.red+=(*k)*pixel.red;
02563             qixel.green+=(*k)*pixel.green;
02564             qixel.blue+=(*k)*pixel.blue;
02565             qixel.opacity+=(*k)*pixel.opacity;
02566             if (image->colorspace == CMYKColorspace)
02567               {
02568                 indexes=GetCacheViewVirtualIndexQueue(image_view);
02569                 qixel.index+=(*k)*(*indexes);
02570               }
02571             k++;
02572           }
02573           if ((channel & RedChannel) != 0)
02574             q->red=RoundToQuantum(qixel.red);
02575           if ((channel & GreenChannel) != 0)
02576             q->green=RoundToQuantum(qixel.green);
02577           if ((channel & BlueChannel) != 0)
02578             q->blue=RoundToQuantum(qixel.blue);
02579           if ((channel & OpacityChannel) != 0)
02580             q->opacity=RoundToQuantum(qixel.opacity);
02581           if (((channel & IndexChannel) != 0) &&
02582               (image->colorspace == CMYKColorspace))
02583             blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
02584         }
02585       else
02586         {
02587           MagickRealType
02588             alpha,
02589             gamma;
02590 
02591           alpha=0.0;
02592           gamma=0.0;
02593           for (i=0; i < (long) width; i++)
02594           {
02595             (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
02596               offset[i].y,&pixel,exception);
02597             alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
02598             qixel.red+=(*k)*alpha*pixel.red;
02599             qixel.green+=(*k)*alpha*pixel.green;
02600             qixel.blue+=(*k)*alpha*pixel.blue;
02601             qixel.opacity+=(*k)*pixel.opacity;
02602             if (image->colorspace == CMYKColorspace)
02603               {
02604                 indexes=GetCacheViewVirtualIndexQueue(image_view);
02605                 qixel.index+=(*k)*alpha*(*indexes);
02606               }
02607             gamma+=(*k)*alpha;
02608             k++;
02609           }
02610           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02611           if ((channel & RedChannel) != 0)
02612             q->red=RoundToQuantum(gamma*qixel.red);
02613           if ((channel & GreenChannel) != 0)
02614             q->green=RoundToQuantum(gamma*qixel.green);
02615           if ((channel & BlueChannel) != 0)
02616             q->blue=RoundToQuantum(gamma*qixel.blue);
02617           if ((channel & OpacityChannel) != 0)
02618             q->opacity=RoundToQuantum(qixel.opacity);
02619           if (((channel & IndexChannel) != 0) &&
02620               (image->colorspace == CMYKColorspace))
02621             blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
02622         }
02623       q++;
02624     }
02625     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
02626       status=MagickFalse;
02627     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02628       {
02629         MagickBooleanType
02630           proceed;
02631 
02632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02633   #pragma omp critical (MagickCore_MotionBlurImageChannel)
02634 #endif
02635         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
02636         if (proceed == MagickFalse)
02637           status=MagickFalse;
02638       }
02639   }
02640   blur_view=DestroyCacheView(blur_view);
02641   image_view=DestroyCacheView(image_view);
02642   kernel=(double *) RelinquishMagickMemory(kernel);
02643   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02644   if (status == MagickFalse)
02645     blur_image=DestroyImage(blur_image);
02646   return(blur_image);
02647 }
02648 
02649 /*
02650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02651 %                                                                             %
02652 %                                                                             %
02653 %                                                                             %
02654 %     P r e v i e w I m a g e                                                 %
02655 %                                                                             %
02656 %                                                                             %
02657 %                                                                             %
02658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02659 %
02660 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
02661 %  processing operation applied with varying parameters.  This may be helpful
02662 %  pin-pointing an appropriate parameter for a particular image processing
02663 %  operation.
02664 %
02665 %  The format of the PreviewImages method is:
02666 %
02667 %      Image *PreviewImages(const Image *image,const PreviewType preview,
02668 %        ExceptionInfo *exception)
02669 %
02670 %  A description of each parameter follows:
02671 %
02672 %    o image: the image.
02673 %
02674 %    o preview: the image processing operation.
02675 %
02676 %    o exception: return any errors or warnings in this structure.
02677 %
02678 */
02679 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
02680   ExceptionInfo *exception)
02681 {
02682 #define NumberTiles  9
02683 #define PreviewImageTag  "Preview/Image"
02684 #define DefaultPreviewGeometry  "204x204+10+10"
02685 
02686   char
02687     factor[MaxTextExtent],
02688     label[MaxTextExtent];
02689 
02690   double
02691     degrees,
02692     gamma,
02693     percentage,
02694     radius,
02695     sigma,
02696     threshold;
02697 
02698   Image
02699     *images,
02700     *montage_image,
02701     *preview_image,
02702     *thumbnail;
02703 
02704   ImageInfo
02705     *preview_info;
02706 
02707   long
02708     y;
02709 
02710   MagickBooleanType
02711     proceed;
02712 
02713   MontageInfo
02714     *montage_info;
02715 
02716   QuantizeInfo
02717     quantize_info;
02718 
02719   RectangleInfo
02720     geometry;
02721 
02722   register long
02723     i,
02724     x;
02725 
02726   unsigned long
02727     colors;
02728 
02729   /*
02730     Open output image file.
02731   */
02732   assert(image != (Image *) NULL);
02733   assert(image->signature == MagickSignature);
02734   if (image->debug != MagickFalse)
02735     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02736   colors=2;
02737   degrees=0.0;
02738   gamma=(-0.2f);
02739   preview_info=AcquireImageInfo();
02740   SetGeometry(image,&geometry);
02741   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
02742     &geometry.width,&geometry.height);
02743   images=NewImageList();
02744   percentage=12.5;
02745   GetQuantizeInfo(&quantize_info);
02746   radius=0.0;
02747   sigma=1.0;
02748   threshold=0.0;
02749   x=0;
02750   y=0;
02751   for (i=0; i < NumberTiles; i++)
02752   {
02753     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
02754     if (thumbnail == (Image *) NULL)
02755       break;
02756     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
02757       (void *) NULL);
02758     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
02759     if (i == (NumberTiles/2))
02760       {
02761         (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
02762         AppendImageToList(&images,thumbnail);
02763         continue;
02764       }
02765     switch (preview)
02766     {
02767       case RotatePreview:
02768       {
02769         degrees+=45.0;
02770         preview_image=RotateImage(thumbnail,degrees,exception);
02771         (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
02772         break;
02773       }
02774       case ShearPreview:
02775       {
02776         degrees+=5.0;
02777         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
02778         (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
02779           degrees,2.0*degrees);
02780         break;
02781       }
02782       case RollPreview:
02783       {
02784         x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
02785         y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
02786         preview_image=RollImage(thumbnail,x,y,exception);
02787         (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
02788         break;
02789       }
02790       case HuePreview:
02791       {
02792         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02793         if (preview_image == (Image *) NULL)
02794           break;
02795         (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
02796           2.0*percentage);
02797         (void) ModulateImage(preview_image,factor);
02798         (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02799         break;
02800       }
02801       case SaturationPreview:
02802       {
02803         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02804         if (preview_image == (Image *) NULL)
02805           break;
02806         (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
02807         (void) ModulateImage(preview_image,factor);
02808         (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02809         break;
02810       }
02811       case BrightnessPreview:
02812       {
02813         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02814         if (preview_image == (Image *) NULL)
02815           break;
02816         (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
02817         (void) ModulateImage(preview_image,factor);
02818         (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
02819         break;
02820       }
02821       case GammaPreview:
02822       default:
02823       {
02824         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02825         if (preview_image == (Image *) NULL)
02826           break;
02827         gamma+=0.4f;
02828         (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
02829         (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
02830         break;
02831       }
02832       case SpiffPreview:
02833       {
02834         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02835         if (preview_image != (Image *) NULL)
02836           for (x=0; x < i; x++)
02837             (void) ContrastImage(preview_image,MagickTrue);
02838         (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
02839         break;
02840       }
02841       case DullPreview:
02842       {
02843         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02844         if (preview_image == (Image *) NULL)
02845           break;
02846         for (x=0; x < i; x++)
02847           (void) ContrastImage(preview_image,MagickFalse);
02848         (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
02849         break;
02850       }
02851       case GrayscalePreview:
02852       {
02853         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02854         if (preview_image == (Image *) NULL)
02855           break;
02856         colors<<=1;
02857         quantize_info.number_colors=colors;
02858         quantize_info.colorspace=GRAYColorspace;
02859         (void) QuantizeImage(&quantize_info,preview_image);
02860         (void) FormatMagickString(label,MaxTextExtent,
02861           "-colorspace gray -colors %ld",colors);
02862         break;
02863       }
02864       case QuantizePreview:
02865       {
02866         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02867         if (preview_image == (Image *) NULL)
02868           break;
02869         colors<<=1;
02870         quantize_info.number_colors=colors;
02871         (void) QuantizeImage(&quantize_info,preview_image);
02872         (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
02873         break;
02874       }
02875       case DespecklePreview:
02876       {
02877         for (x=0; x < (i-1); x++)
02878         {
02879           preview_image=DespeckleImage(thumbnail,exception);
02880           if (preview_image == (Image *) NULL)
02881             break;
02882           thumbnail=DestroyImage(thumbnail);
02883           thumbnail=preview_image;
02884         }
02885         preview_image=DespeckleImage(thumbnail,exception);
02886         if (preview_image == (Image *) NULL)
02887           break;
02888         (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
02889         break;
02890       }
02891       case ReduceNoisePreview:
02892       {
02893         preview_image=ReduceNoiseImage(thumbnail,radius,exception);
02894         (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
02895         break;
02896       }
02897       case AddNoisePreview:
02898       {
02899         switch ((int) i)
02900         {
02901           case 0:
02902           {
02903             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
02904             break;
02905           }
02906           case 1:
02907           {
02908             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
02909             break;
02910           }
02911           case 2:
02912           {
02913             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
02914             break;
02915           }
02916           case 3:
02917           {
02918             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
02919             break;
02920           }
02921           case 4:
02922           {
02923             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
02924             break;
02925           }
02926           case 5:
02927           {
02928             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
02929             break;
02930           }
02931           default:
02932           {
02933             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
02934             break;
02935           }
02936         }
02937         preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
02938         (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
02939         break;
02940       }
02941       case SharpenPreview:
02942       {
02943         preview_image=SharpenImage(thumbnail,radius,sigma,exception);
02944         (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
02945           sigma);
02946         break;
02947       }
02948       case BlurPreview:
02949       {
02950         preview_image=BlurImage(thumbnail,radius,sigma,exception);
02951         (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
02952           sigma);
02953         break;
02954       }
02955       case ThresholdPreview:
02956       {
02957         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02958         if (preview_image == (Image *) NULL)
02959           break;
02960         (void) BilevelImage(thumbnail,
02961           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
02962         (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
02963           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
02964         break;
02965       }
02966       case EdgeDetectPreview:
02967       {
02968         preview_image=EdgeImage(thumbnail,radius,exception);
02969         (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
02970         break;
02971       }
02972       case SpreadPreview:
02973       {
02974         preview_image=SpreadImage(thumbnail,radius,exception);
02975         (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
02976         break;
02977       }
02978       case SolarizePreview:
02979       {
02980         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02981         if (preview_image == (Image *) NULL)
02982           break;
02983         (void) SolarizeImage(preview_image,(double) QuantumRange*
02984           percentage/100.0);
02985         (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
02986           (QuantumRange*percentage)/100.0);
02987         break;
02988       }
02989       case ShadePreview:
02990       {
02991         degrees+=10.0;
02992         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
02993           exception);
02994         (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
02995           degrees);
02996         break;
02997       }
02998       case RaisePreview:
02999       {
03000         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03001         if (preview_image == (Image *) NULL)
03002           break;
03003         geometry.width=(unsigned long) (2*i+2);
03004         geometry.height=(unsigned long) (2*i+2);
03005         geometry.x=i/2;
03006         geometry.y=i/2;
03007         (void) RaiseImage(preview_image,&geometry,MagickTrue);
03008         (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
03009           geometry.width,geometry.height,geometry.x,geometry.y);
03010         break;
03011       }
03012       case SegmentPreview:
03013       {
03014         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03015         if (preview_image == (Image *) NULL)
03016           break;
03017         threshold+=0.4f;
03018         (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
03019           threshold);
03020         (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
03021           threshold,threshold);
03022         break;
03023       }
03024       case SwirlPreview:
03025       {
03026         preview_image=SwirlImage(thumbnail,degrees,exception);
03027         (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
03028         degrees+=45.0;
03029         break;
03030       }
03031       case ImplodePreview:
03032       {
03033         degrees+=0.1f;
03034         preview_image=ImplodeImage(thumbnail,degrees,exception);
03035         (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
03036         break;
03037       }
03038       case WavePreview:
03039       {
03040         degrees+=5.0f;
03041         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
03042         (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
03043           2.0*degrees);
03044         break;
03045       }
03046       case OilPaintPreview:
03047       {
03048         preview_image=OilPaintImage(thumbnail,(double) radius,exception);
03049         (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
03050         break;
03051       }
03052       case CharcoalDrawingPreview:
03053       {
03054         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
03055           exception);
03056         (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
03057           sigma);
03058         break;
03059       }
03060       case JPEGPreview:
03061       {
03062         char
03063           filename[MaxTextExtent];
03064 
03065         int
03066           file;
03067 
03068         MagickBooleanType
03069           status;
03070 
03071         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
03072         if (preview_image == (Image *) NULL)
03073           break;
03074         preview_info->quality=(unsigned long) percentage;
03075         (void) FormatMagickString(factor,MaxTextExtent,"%lu",
03076           preview_info->quality);
03077         file=AcquireUniqueFileResource(filename);
03078         if (file != -1)
03079           file=close(file)-1;
03080         (void) FormatMagickString(preview_image->filename,MaxTextExtent,
03081           "jpeg:%s",filename);
03082         status=WriteImage(preview_info,preview_image);
03083         if (status != MagickFalse)
03084           {
03085             Image
03086               *quality_image;
03087 
03088             (void) CopyMagickString(preview_info->filename,
03089               preview_image->filename,MaxTextExtent);
03090             quality_image=ReadImage(preview_info,exception);
03091             if (quality_image != (Image *) NULL)
03092               {
03093                 preview_image=DestroyImage(preview_image);
03094                 preview_image=quality_image;
03095               }
03096           }
03097         (void) RelinquishUniqueFileResource(preview_image->filename);
03098         if ((GetBlobSize(preview_image)/1024) >= 1024)
03099           (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
03100             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
03101             1024.0/1024.0);
03102         else
03103           if (GetBlobSize(preview_image) >= 1024)
03104             (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
03105               factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
03106               1024.0);
03107           else
03108             (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
03109               factor,(unsigned long) GetBlobSize(thumbnail));
03110         break;
03111       }
03112     }
03113     thumbnail=DestroyImage(thumbnail);
03114     percentage+=12.5;
03115     radius+=0.5;
03116     sigma+=0.25;
03117     if (preview_image == (Image *) NULL)
03118       break;
03119     (void) DeleteImageProperty(preview_image,"label");
03120     (void) SetImageProperty(preview_image,"label",label);
03121     AppendImageToList(&images,preview_image);
03122     proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
03123     if (proceed == MagickFalse)
03124       break;
03125   }
03126   if (images == (Image *) NULL)
03127     {
03128       preview_info=DestroyImageInfo(preview_info);
03129       return((Image *) NULL);
03130     }
03131   /*
03132     Create the montage.
03133   */
03134   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
03135   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
03136   montage_info->shadow=MagickTrue;
03137   (void) CloneString(&montage_info->tile,"3x3");
03138   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
03139   (void) CloneString(&montage_info->frame,DefaultTileFrame);
03140   montage_image=MontageImages(images,montage_info,exception);
03141   montage_info=DestroyMontageInfo(montage_info);
03142   images=DestroyImageList(images);
03143   if (montage_image == (Image *) NULL)
03144     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03145   if (montage_image->montage != (char *) NULL)
03146     {
03147       /*
03148         Free image directory.
03149       */
03150       montage_image->montage=(char *) RelinquishMagickMemory(
03151         montage_image->montage);
03152       if (image->directory != (char *) NULL)
03153         montage_image->directory=(char *) RelinquishMagickMemory(
03154           montage_image->directory);
03155     }
03156   preview_info=DestroyImageInfo(preview_info);
03157   return(montage_image);
03158 }
03159 
03160 /*
03161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03162 %                                                                             %
03163 %                                                                             %
03164 %                                                                             %
03165 %     R a d i a l B l u r I m a g e                                           %
03166 %                                                                             %
03167 %                                                                             %
03168 %                                                                             %
03169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03170 %
03171 %  RadialBlurImage() applies a radial blur to the image.
03172 %
03173 %  Andrew Protano contributed this effect.
03174 %
03175 %  The format of the RadialBlurImage method is:
03176 %
03177 %    Image *RadialBlurImage(const Image *image,const double angle,
03178 %      ExceptionInfo *exception)
03179 %    Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
03180 %      const double angle,ExceptionInfo *exception)
03181 %
03182 %  A description of each parameter follows:
03183 %
03184 %    o image: the image.
03185 %
03186 %    o channel: the channel type.
03187 %
03188 %    o angle: the angle of the radial blur.
03189 %
03190 %    o exception: return any errors or warnings in this structure.
03191 %
03192 */
03193 
03194 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
03195   ExceptionInfo *exception)
03196 {
03197   Image
03198     *blur_image;
03199 
03200   blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
03201   return(blur_image);
03202 }
03203 
03204 MagickExport Image *RadialBlurImageChannel(const Image *image,
03205   const ChannelType channel,const double angle,ExceptionInfo *exception)
03206 {
03207   Image
03208     *blur_image;
03209 
03210   long
03211     progress,
03212     y;
03213 
03214   MagickBooleanType
03215     status;
03216 
03217   MagickPixelPacket
03218     bias;
03219 
03220   MagickRealType
03221     blur_radius,
03222     *cos_theta,
03223     offset,
03224     *sin_theta,
03225     theta;
03226 
03227   PointInfo
03228     blur_center;
03229 
03230   register long
03231     i;
03232 
03233   unsigned long
03234     n;
03235 
03236   CacheView
03237     *blur_view,
03238     *image_view;
03239 
03240   /*
03241     Allocate blur image.
03242   */
03243   assert(image != (Image *) NULL);
03244   assert(image->signature == MagickSignature);
03245   if (image->debug != MagickFalse)
03246     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03247   assert(exception != (ExceptionInfo *) NULL);
03248   assert(exception->signature == MagickSignature);
03249   blur_image=CloneImage(image,0,0,MagickTrue,exception);
03250   if (blur_image == (Image *) NULL)
03251     return((Image *) NULL);
03252   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
03253     {
03254       InheritException(exception,&blur_image->exception);
03255       blur_image=DestroyImage(blur_image);
03256       return((Image *) NULL);
03257     }
03258   blur_center.x=(double) image->columns/2.0;
03259   blur_center.y=(double) image->rows/2.0;
03260   blur_radius=hypot(blur_center.x,blur_center.y);
03261   n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
03262     2UL);
03263   theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
03264   cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
03265     sizeof(*cos_theta));
03266   sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
03267     sizeof(*sin_theta));
03268   if ((cos_theta == (MagickRealType *) NULL) ||
03269       (sin_theta == (MagickRealType *) NULL))
03270     {
03271       blur_image=DestroyImage(blur_image);
03272       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03273     }
03274   offset=theta*(MagickRealType) (n-1)/2.0;
03275   for (i=0; i < (long) n; i++)
03276   {
03277     cos_theta[i]=cos((double) (theta*i-offset));
03278     sin_theta[i]=sin((double) (theta*i-offset));
03279   }
03280   /*
03281     Radial blur image.
03282   */
03283   status=MagickTrue;
03284   progress=0;
03285   GetMagickPixelPacket(image,&bias);
03286   image_view=AcquireCacheView(image);
03287   blur_view=AcquireCacheView(blur_image);
03288 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03289   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03290 #endif
03291   for (y=0; y < (long) blur_image->rows; y++)
03292   {
03293     register const IndexPacket
03294       *__restrict indexes;
03295 
03296     register IndexPacket
03297       *__restrict blur_indexes;
03298 
03299     register long
03300       x;
03301 
03302     register PixelPacket
03303       *__restrict q;
03304 
03305     if (status == MagickFalse)
03306       continue;
03307     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
03308       exception);
03309     if (q == (PixelPacket *) NULL)
03310       {
03311         status=MagickFalse;
03312         continue;
03313       }
03314     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
03315     for (x=0; x < (long) blur_image->columns; x++)
03316     {
03317       MagickPixelPacket
03318         qixel;
03319 
03320       MagickRealType
03321         normalize,
03322         radius;
03323 
03324       PixelPacket
03325         pixel;
03326 
03327       PointInfo
03328         center;
03329 
03330       register long
03331         i;
03332 
03333       unsigned long
03334         step;
03335 
03336       center.x=(double) x-blur_center.x;
03337       center.y=(double) y-blur_center.y;
03338       radius=hypot((double) center.x,center.y);
03339       if (radius == 0)
03340         step=1;
03341       else
03342         {
03343           step=(unsigned long) (blur_radius/radius);
03344           if (step == 0)
03345             step=1;
03346           else
03347             if (step >= n)
03348               step=n-1;
03349         }
03350       normalize=0.0;
03351       qixel=bias;
03352       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
03353         {
03354           for (i=0; i < (long) n; i+=step)
03355           {
03356             (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
03357               center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
03358               blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
03359               &pixel,exception);
03360             qixel.red+=pixel.red;
03361             qixel.green+=pixel.green;
03362             qixel.blue+=pixel.blue;
03363             qixel.opacity+=pixel.opacity;
03364             if (image->colorspace == CMYKColorspace)
03365               {
03366                 indexes=GetCacheViewVirtualIndexQueue(image_view);
03367                 qixel.index+=(*indexes);
03368               }
03369             normalize+=1.0;
03370           }
03371           normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
03372             normalize);
03373           if ((channel & RedChannel) != 0)
03374             q->red=RoundToQuantum(normalize*qixel.red);
03375           if ((channel & GreenChannel) != 0)
03376             q->green=RoundToQuantum(normalize*qixel.green);
03377           if ((channel & BlueChannel) != 0)
03378             q->blue=RoundToQuantum(normalize*qixel.blue);
03379           if ((channel & OpacityChannel) != 0)
03380             q->opacity=RoundToQuantum(normalize*qixel.opacity);
03381           if (((channel & IndexChannel) != 0) &&
03382               (image->colorspace == CMYKColorspace))
03383             blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
03384         }
03385       else
03386         {
03387           MagickRealType
03388             alpha,
03389             gamma;
03390 
03391           alpha=1.0;
03392           gamma=0.0;
03393           for (i=0; i < (long) n; i+=step)
03394           {
03395             (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
03396               center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
03397               blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
03398               &pixel,exception);
03399             alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
03400             qixel.red+=alpha*pixel.red;
03401             qixel.green+=alpha*pixel.green;
03402             qixel.blue+=alpha*pixel.blue;
03403             qixel.opacity+=pixel.opacity;
03404             if (image->colorspace == CMYKColorspace)
03405               {
03406                 indexes=GetCacheViewVirtualIndexQueue(image_view);
03407                 qixel.index+=alpha*(*indexes);
03408               }
03409             gamma+=alpha;
03410             normalize+=1.0;
03411           }
03412           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03413           normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
03414             normalize);
03415           if ((channel & RedChannel) != 0)
03416             q->red=RoundToQuantum(gamma*qixel.red);
03417           if ((channel & GreenChannel) != 0)
03418             q->green=RoundToQuantum(gamma*qixel.green);
03419           if ((channel & BlueChannel) != 0)
03420             q->blue=RoundToQuantum(gamma*qixel.blue);
03421           if ((channel & OpacityChannel) != 0)
03422             q->opacity=RoundToQuantum(normalize*qixel.opacity);
03423           if (((channel & IndexChannel) != 0) &&
03424               (image->colorspace == CMYKColorspace))
03425             blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
03426         }
03427       q++;
03428     }
03429     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
03430       status=MagickFalse;
03431     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03432       {
03433         MagickBooleanType
03434           proceed;
03435 
03436 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03437   #pragma omp critical (MagickCore_RadialBlurImageChannel)
03438 #endif
03439         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
03440         if (proceed == MagickFalse)
03441           status=MagickFalse;
03442       }
03443   }
03444   blur_view=DestroyCacheView(blur_view);
03445   image_view=DestroyCacheView(image_view);
03446   cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
03447   sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
03448   if (status == MagickFalse)
03449     blur_image=DestroyImage(blur_image);
03450   return(blur_image);
03451 }
03452 
03453 /*
03454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03455 %                                                                             %
03456 %                                                                             %
03457 %                                                                             %
03458 %     R e d u c e N o i s e I m a g e                                         %
03459 %                                                                             %
03460 %                                                                             %
03461 %                                                                             %
03462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03463 %
03464 %  ReduceNoiseImage() smooths the contours of an image while still preserving
03465 %  edge information.  The algorithm works by replacing each pixel with its
03466 %  neighbor closest in value.  A neighbor is defined by radius.  Use a radius
03467 %  of 0 and ReduceNoise() selects a suitable radius for you.
03468 %
03469 %  The format of the ReduceNoiseImage method is:
03470 %
03471 %      Image *ReduceNoiseImage(const Image *image,const double radius,
03472 %        ExceptionInfo *exception)
03473 %
03474 %  A description of each parameter follows:
03475 %
03476 %    o image: the image.
03477 %
03478 %    o radius: the radius of the pixel neighborhood.
03479 %
03480 %    o exception: return any errors or warnings in this structure.
03481 %
03482 */
03483 
03484 static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
03485 {
03486   MagickPixelPacket
03487     pixel;
03488 
03489   register long
03490     channel;
03491 
03492   register MedianSkipList
03493     *list;
03494 
03495   unsigned long
03496     center,
03497     color,
03498     count,
03499     previous,
03500     next;
03501 
03502   unsigned short
03503     channels[5];
03504 
03505   /*
03506     Finds the median value for each of the color.
03507   */
03508   center=pixel_list->center;
03509   for (channel=0; channel < 5; channel++)
03510   {
03511     list=pixel_list->lists+channel;
03512     color=65536UL;
03513     next=list->nodes[color].next[0];
03514     count=0;
03515     do
03516     {
03517       previous=color;
03518       color=next;
03519       next=list->nodes[color].next[0];
03520       count+=list->nodes[color].count;
03521     }
03522     while (count <= center);
03523     if ((previous == 65536UL) && (next != 65536UL))
03524       color=next;
03525     else
03526       if ((previous != 65536UL) && (next == 65536UL))
03527         color=previous;
03528     channels[channel]=(unsigned short) color;
03529   }
03530   GetMagickPixelPacket((const Image *) NULL,&pixel);
03531   pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
03532   pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
03533   pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
03534   pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
03535   pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
03536   return(pixel);
03537 }
03538 
03539 MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
03540   ExceptionInfo *exception)
03541 {
03542 #define ReduceNoiseImageTag  "ReduceNoise/Image"
03543 
03544   Image
03545     *noise_image;
03546 
03547   long
03548     progress,
03549     y;
03550 
03551   MagickBooleanType
03552     status;
03553 
03554   MedianPixelList
03555     **pixel_list;
03556 
03557   unsigned long
03558     width;
03559 
03560   CacheView
03561     *image_view,
03562     *noise_view;
03563 
03564   /*
03565     Initialize noise image attributes.
03566   */
03567   assert(image != (Image *) NULL);
03568   assert(image->signature == MagickSignature);
03569   if (image->debug != MagickFalse)
03570     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03571   assert(exception != (ExceptionInfo *) NULL);
03572   assert(exception->signature == MagickSignature);
03573   width=GetOptimalKernelWidth2D(radius,0.5);
03574   if ((image->columns < width) || (image->rows < width))
03575     ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
03576   noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
03577     exception);
03578   if (noise_image == (Image *) NULL)
03579     return((Image *) NULL);
03580   if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
03581     {
03582       InheritException(exception,&noise_image->exception);
03583       noise_image=DestroyImage(noise_image);
03584       return((Image *) NULL);
03585     }
03586   pixel_list=AcquireMedianPixelListThreadSet(width);
03587   if (pixel_list == (MedianPixelList **) NULL)
03588     {
03589       noise_image=DestroyImage(noise_image);
03590       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03591     }
03592   /*
03593     Reduce noise image.
03594   */
03595   status=MagickTrue;
03596   progress=0;
03597   image_view=AcquireCacheView(image);
03598   noise_view=AcquireCacheView(noise_image);
03599 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03600   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03601 #endif
03602   for (y=0; y < (long) noise_image->rows; y++)
03603   {
03604     register const IndexPacket
03605       *__restrict indexes;
03606 
03607     register const PixelPacket
03608       *__restrict p;
03609 
03610     register IndexPacket
03611       *__restrict noise_indexes;
03612 
03613     register long
03614       id,
03615       x;
03616 
03617     register PixelPacket
03618       *__restrict q;
03619 
03620     if (status == MagickFalse)
03621       continue;
03622     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
03623       2L),image->columns+width,width,exception);
03624     q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
03625       exception);
03626     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
03627       {
03628         status=MagickFalse;
03629         continue;
03630       }
03631     indexes=GetCacheViewVirtualIndexQueue(image_view);
03632     noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
03633     id=GetOpenMPThreadId();
03634     for (x=0; x < (long) noise_image->columns; x++)
03635     {
03636       MagickPixelPacket
03637         pixel;
03638 
03639       register const PixelPacket
03640         *__restrict r;
03641 
03642       register const IndexPacket
03643         *__restrict s;
03644 
03645       register long
03646         u,
03647         v;
03648 
03649       r=p;
03650       s=indexes+x;
03651       ResetMedianPixelList(pixel_list[id]);
03652       for (v=0; v < (long) width; v++)
03653       {
03654         for (u=0; u < (long) width; u++)
03655           InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
03656         r+=image->columns+width;
03657         s+=image->columns+width;
03658       }
03659       pixel=GetNonpeakMedianPixelList(pixel_list[id]);
03660       SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
03661       p++;
03662       q++;
03663     }
03664     if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
03665       status=MagickFalse;
03666     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03667       {
03668         MagickBooleanType
03669           proceed;
03670 
03671 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03672   #pragma omp critical (MagickCore_ReduceNoiseImage)
03673 #endif
03674         proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
03675           image->rows);
03676         if (proceed == MagickFalse)
03677           status=MagickFalse;
03678       }
03679   }
03680   noise_view=DestroyCacheView(noise_view);
03681   image_view=DestroyCacheView(image_view);
03682   pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
03683   return(noise_image);
03684 }
03685 
03686 /*
03687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03688 %                                                                             %
03689 %                                                                             %
03690 %                                                                             %
03691 %     S e l e c t i v e B l u r I m a g e                                     %
03692 %                                                                             %
03693 %                                                                             %
03694 %                                                                             %
03695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03696 %
03697 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
03698 %  It is similar to the unsharpen mask that sharpens everything with contrast
03699 %  above a certain threshold.
03700 %
03701 %  The format of the SelectiveBlurImage method is:
03702 %
03703 %      Image *SelectiveBlurImage(const Image *image,const double radius,
03704 %        const double sigma,const double threshold,ExceptionInfo *exception)
03705 %      Image *SelectiveBlurImageChannel(const Image *image,
03706 %        const ChannelType channel,const double radius,const double sigma,
03707 %        const double threshold,ExceptionInfo *exception)
03708 %
03709 %  A description of each parameter follows:
03710 %
03711 %    o image: the image.
03712 %
03713 %    o channel: the channel type.
03714 %
03715 %    o radius: the radius of the Gaussian, in pixels, not counting the center
03716 %      pixel.
03717 %
03718 %    o sigma: the standard deviation of the Gaussian, in pixels.
03719 %
03720 %    o threshold: only pixels within this contrast threshold are included
03721 %      in the blur operation.
03722 %
03723 %    o exception: return any errors or warnings in this structure.
03724 %
03725 */
03726 
03727 static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
03728   const PixelPacket *q,const double threshold)
03729 {
03730   if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
03731     return(MagickTrue);
03732   return(MagickFalse);
03733 }
03734 
03735 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
03736   const double sigma,const double threshold,ExceptionInfo *exception)
03737 {
03738   Image
03739     *blur_image;
03740 
03741   blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
03742     threshold,exception);
03743   return(blur_image);
03744 }
03745 
03746 MagickExport Image *SelectiveBlurImageChannel(const Image *image,
03747   const ChannelType channel,const double radius,const double sigma,
03748   const double threshold,ExceptionInfo *exception)
03749 {
03750 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
03751 
03752   double
03753     alpha,
03754     *kernel;
03755 
03756   Image
03757     *blur_image;
03758 
03759   long
03760     progress,
03761     v,
03762     y;
03763 
03764   MagickBooleanType
03765     status;
03766 
03767   MagickPixelPacket
03768     bias;
03769 
03770   register long
03771     i,
03772     u;
03773 
03774   unsigned long
03775     width;
03776 
03777   CacheView
03778     *blur_view,
03779     *image_view;
03780 
03781   /*
03782     Initialize blur image attributes.
03783   */
03784   assert(image != (Image *) NULL);
03785   assert(image->signature == MagickSignature);
03786   if (image->debug != MagickFalse)
03787     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03788   assert(exception != (ExceptionInfo *) NULL);
03789   assert(exception->signature == MagickSignature);
03790   width=GetOptimalKernelWidth1D(radius,sigma);
03791   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
03792   if (kernel == (double *) NULL)
03793     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03794   i=0;
03795   for (v=(-((long) width/2)); v <= (long) (width/2); v++)
03796   {
03797     for (u=(-((long) width/2)); u <= (long) (width/2); u++)
03798     {
03799       alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
03800       kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
03801       i++;
03802     }
03803   }
03804   if (image->debug != MagickFalse)
03805     {
03806       char
03807         format[MaxTextExtent],
03808         *message;
03809 
03810       long
03811         u,
03812         v;
03813 
03814       register const double
03815         *k;
03816 
03817       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
03818         "  SelectiveBlurImage with %ldx%ld kernel:",width,width);
03819       message=AcquireString("");
03820       k=kernel;
03821       for (v=0; v < (long) width; v++)
03822       {
03823         *message='\0';
03824         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
03825         (void) ConcatenateString(&message,format);
03826         for (u=0; u < (long) width; u++)
03827         {
03828           (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
03829           (void) ConcatenateString(&message,format);
03830         }
03831         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
03832       }
03833       message=DestroyString(message);
03834     }
03835   blur_image=CloneImage(image,0,0,MagickTrue,exception);
03836   if (blur_image == (Image *) NULL)
03837     return((Image *) NULL);
03838   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
03839     {
03840       InheritException(exception,&blur_image->exception);
03841       blur_image=DestroyImage(blur_image);
03842       return((Image *) NULL);
03843     }
03844   /*
03845     Threshold blur image.
03846   */
03847   status=MagickTrue;
03848   progress=0;
03849   GetMagickPixelPacket(image,&bias);
03850   SetMagickPixelPacketBias(image,&bias);
03851   image_view=AcquireCacheView(image);
03852   blur_view=AcquireCacheView(blur_image);
03853 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03854   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03855 #endif
03856   for (y=0; y < (long) image->rows; y++)
03857   {
03858     MagickBooleanType
03859       sync;
03860 
03861     MagickRealType
03862       gamma;
03863 
03864     register const IndexPacket
03865       *__restrict indexes;
03866 
03867     register const PixelPacket
03868       *__restrict p;
03869 
03870     register IndexPacket
03871       *__restrict blur_indexes;
03872 
03873     register long
03874       x;
03875 
03876     register PixelPacket
03877       *__restrict q;
03878 
03879     if (status == MagickFalse)
03880       continue;
03881     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
03882       2L),image->columns+width,width,exception);
03883     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
03884       exception);
03885     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
03886       {
03887         status=MagickFalse;
03888         continue;
03889       }
03890     indexes=GetCacheViewVirtualIndexQueue(image_view);
03891     blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
03892     for (x=0; x < (long) image->columns; x++)
03893     {
03894       long
03895         j,
03896         v;
03897 
03898       MagickPixelPacket
03899         pixel;
03900 
03901       register const double
03902         *__restrict k;
03903 
03904       register long
03905         u;
03906 
03907       pixel=bias;
03908       k=kernel;
03909       gamma=0.0;
03910       j=0;
03911       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
03912         {
03913           for (v=0; v < (long) width; v++)
03914           {
03915             for (u=0; u < (long) width; u++)
03916             {
03917               if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03918                 {
03919                   pixel.red+=(*k)*(p+u+j)->red;
03920                   pixel.green+=(*k)*(p+u+j)->green;
03921                   pixel.blue+=(*k)*(p+u+j)->blue;
03922                   gamma+=(*k);
03923                   k++;
03924                 }
03925             }
03926             j+=image->columns+width;
03927           }
03928           if (gamma != 0.0)
03929             {
03930               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03931               if ((channel & RedChannel) != 0)
03932                 q->red=RoundToQuantum(gamma*pixel.red);
03933               if ((channel & GreenChannel) != 0)
03934                 q->green=RoundToQuantum(gamma*pixel.green);
03935               if ((channel & BlueChannel) != 0)
03936                 q->blue=RoundToQuantum(gamma*pixel.blue);
03937             }
03938           if ((channel & OpacityChannel) != 0)
03939             {
03940               gamma=0.0;
03941               j=0;
03942               for (v=0; v < (long) width; v++)
03943               {
03944                 for (u=0; u < (long) width; u++)
03945                 {
03946                   if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03947                     {
03948                       pixel.opacity+=(*k)*(p+u+j)->opacity;
03949                       gamma+=(*k);
03950                       k++;
03951                     }
03952                 }
03953                 j+=image->columns+width;
03954               }
03955               if (gamma != 0.0)
03956                 {
03957                   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
03958                     gamma);
03959                   q->opacity=RoundToQuantum(gamma*pixel.opacity);
03960                 }
03961             }
03962           if (((channel & IndexChannel) != 0) &&
03963               (image->colorspace == CMYKColorspace))
03964             {
03965               gamma=0.0;
03966               j=0;
03967               for (v=0; v < (long) width; v++)
03968               {
03969                 for (u=0; u < (long) width; u++)
03970                 {
03971                   if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03972                     {
03973                       pixel.index+=(*k)*indexes[x+u+j];
03974                       gamma+=(*k);
03975                       k++;
03976                     }
03977                 }
03978                 j+=image->columns+width;
03979               }
03980               if (gamma != 0.0)
03981                 {
03982                   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
03983                     gamma);
03984                   blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
03985                 }
03986             }
03987         }
03988       else
03989         {
03990           MagickRealType
03991             alpha;
03992 
03993           for (v=0; v < (long) width; v++)
03994           {
03995             for (u=0; u < (long) width; u++)
03996             {
03997               if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
03998                 {
03999                   alpha=(MagickRealType) (QuantumScale*(QuantumRange-
04000                     (p+u+j)->opacity));
04001                   pixel.red+=(*k)*alpha*(p+u+j)->red;
04002                   pixel.green+=(*k)*alpha*(p+u+j)->green;
04003                   pixel.blue+=(*k)*alpha*(p+u+j)->blue;
04004                   pixel.opacity+=(*k)*(p+u+j)->opacity;
04005                   gamma+=(*k)*alpha;
04006                   k++;
04007                 }
04008             }
04009             j+=image->columns+width;
04010           }
04011           if (gamma != 0.0)
04012             {
04013               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
04014               if ((channel & RedChannel) != 0)
04015                 q->red=RoundToQuantum(gamma*pixel.red);
04016               if ((channel & GreenChannel) != 0)
04017                 q->green=RoundToQuantum(gamma*pixel.green);
04018               if ((channel & BlueChannel) != 0)
04019                 q->blue=RoundToQuantum(gamma*pixel.blue);
04020             }
04021           if ((channel & OpacityChannel) != 0)
04022             {
04023               gamma=0.0;
04024               j=0;
04025               for (v=0; v < (long) width; v++)
04026               {
04027                 for (u=0; u < (long) width; u++)
04028                 {
04029                   if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
04030                     {
04031                       pixel.opacity+=(*k)*(p+u+j)->opacity;
04032                       gamma+=(*k);
04033                       k++;
04034                     }
04035                 }
04036                 j+=image->columns+width;
04037               }
04038               if (gamma != 0.0)
04039                 {
04040                   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
04041                     gamma);
04042                   q->opacity=RoundToQuantum(pixel.opacity);
04043                 }
04044             }
04045           if (((channel & IndexChannel) != 0) &&
04046               (image->colorspace == CMYKColorspace))
04047             {
04048               gamma=0.0;
04049               j=0;
04050               for (v=0; v < (long) width; v++)
04051               {
04052                 for (u=0; u < (long) width; u++)
04053                 {
04054                   if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
04055                     {
04056                       alpha=(MagickRealType) (QuantumScale*(QuantumRange-
04057                         (p+u+j)->opacity));
04058                       pixel.index+=(*k)*alpha*indexes[x+u+j];
04059                       gamma+=(*k);
04060                       k++;
04061                     }
04062                 }
04063                 j+=image->columns+width;
04064               }
04065               if (gamma != 0.0)
04066                 {
04067                   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
04068                     gamma);
04069                   blur_indexes[x]=RoundToQuantum(gamma*pixel.index);
04070                 }
04071             }
04072         }
04073       p++;
04074       q++;
04075     }
04076     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
04077     if (sync == MagickFalse)
04078       status=MagickFalse;
04079     if (image->progress_monitor != (MagickProgressMonitor) NULL)
04080       {
04081         MagickBooleanType
04082           proceed;
04083 
04084 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04085   #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
04086 #endif
04087         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
04088           image->rows);
04089         if (proceed == MagickFalse)
04090           status=MagickFalse;
04091       }
04092   }
04093   blur_image->type=image->type;
04094   blur_view=DestroyCacheView(blur_view);
04095   image_view=DestroyCacheView(image_view);
04096   kernel=(double *) RelinquishMagickMemory(kernel);
04097   if (status == MagickFalse)
04098     blur_image=DestroyImage(blur_image);
04099   return(blur_image);
04100 }
04101 
04102 /*
04103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04104 %                                                                             %
04105 %                                                                             %
04106 %                                                                             %
04107 %     S h a d e I m a g e                                                     %
04108 %                                                                             %
04109 %                                                                             %
04110 %                                                                             %
04111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04112 %
04113 %  ShadeImage() shines a distant light on an image to create a
04114 %  three-dimensional effect. You control the positioning of the light with
04115 %  azimuth and elevation; azimuth is measured in degrees off the x axis
04116 %  and elevation is measured in pixels above the Z axis.
04117 %
04118 %  The format of the ShadeImage method is:
04119 %
04120 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
04121 %        const double azimuth,const double elevation,ExceptionInfo *exception)
04122 %
04123 %  A description of each parameter follows:
04124 %
04125 %    o image: the image.
04126 %
04127 %    o gray: A value other than zero shades the intensity of each pixel.
04128 %
04129 %    o azimuth, elevation:  Define the light source direction.
04130 %
04131 %    o exception: return any errors or warnings in this structure.
04132 %
04133 */
04134 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
04135   const double azimuth,const double elevation,ExceptionInfo *exception)
04136 {
04137 #define ShadeImageTag  "Shade/Image"
04138 
04139   Image
04140     *shade_image;
04141 
04142   long
04143     progress,
04144     y;
04145 
04146   MagickBooleanType
04147     status;
04148 
04149   PrimaryInfo
04150     light;
04151 
04152   CacheView
04153     *image_view,
04154     *shade_view;
04155 
04156   /*
04157     Initialize shaded image attributes.
04158   */
04159   assert(image != (const Image *) NULL);
04160   assert(image->signature == MagickSignature);
04161   if (image->debug != MagickFalse)
04162     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
04163   assert(exception != (ExceptionInfo *) NULL);
04164   assert(exception->signature == MagickSignature);
04165   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
04166   if (shade_image == (Image *) NULL)
04167     return((Image *) NULL);
04168   if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
04169     {
04170       InheritException(exception,&shade_image->exception);
04171       shade_image=DestroyImage(shade_image);
04172       return((Image *) NULL);
04173     }
04174   /*
04175     Compute the light vector.
04176   */
04177   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
04178     cos(DegreesToRadians(elevation));
04179   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
04180     cos(DegreesToRadians(elevation));
04181   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
04182   /*
04183     Shade image.
04184   */
04185   status=MagickTrue;
04186   progress=0;
04187   image_view=AcquireCacheView(image);
04188   shade_view=AcquireCacheView(shade_image);
04189 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04190   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
04191 #endif
04192   for (y=0; y < (long) image->rows; y++)
04193   {
04194     MagickRealType
04195       distance,
04196       normal_distance,
04197       shade;
04198 
04199     PrimaryInfo
04200       normal;
04201 
04202     register const PixelPacket
04203       *__restrict p,
04204       *__restrict s0,
04205       *__restrict s1,
04206       *__restrict s2;
04207 
04208     register long
04209       x;
04210 
04211     register PixelPacket
04212       *__restrict q;
04213 
04214     if (status == MagickFalse)
04215       continue;
04216     p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
04217     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
04218       exception);
04219     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
04220       {
04221         status=MagickFalse;
04222         continue;
04223       }
04224     /*
04225       Shade this row of pixels.
04226     */
04227     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
04228     s0=p+1;
04229     s1=s0+image->columns+2;
04230     s2=s1+image->columns+2;
04231     for (x=0; x < (long) image->columns; x++)
04232     {
04233       /*
04234         Determine the surface normal and compute shading.
04235       */
04236       normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
04237         PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
04238         PixelIntensity(s2+1));
04239       normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
04240         PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
04241         PixelIntensity(s0+1));
04242       if ((normal.x == 0.0) && (normal.y == 0.0))
04243         shade=light.z;
04244       else
04245         {
04246           shade=0.0;
04247           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04248           if (distance > MagickEpsilon)
04249             {
04250               normal_distance=
04251                 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04252               if (normal_distance > (MagickEpsilon*MagickEpsilon))
04253                 shade=distance/sqrt((double) normal_distance);
04254             }
04255         }
04256       if (gray != MagickFalse)
04257         {
04258           q->red=(Quantum) shade;
04259           q->green=(Quantum) shade;
04260           q->blue=(Quantum) shade;
04261         }
04262       else
04263         {
04264           q->red=RoundToQuantum(QuantumScale*shade*s1->red);
04265           q->green=RoundToQuantum(QuantumScale*shade*s1->green);
04266           q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
04267         }
04268       q->opacity=s1->opacity;
04269       s0++;
04270       s1++;
04271       s2++;
04272       q++;
04273     }
04274     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
04275       status=MagickFalse;
04276     if (image->progress_monitor != (MagickProgressMonitor) NULL)
04277       {
04278         MagickBooleanType
04279           proceed;
04280 
04281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04282   #pragma omp critical (MagickCore_ShadeImage)
04283 #endif
04284         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
04285         if (proceed == MagickFalse)
04286           status=MagickFalse;
04287       }
04288   }
04289   shade_view=DestroyCacheView(shade_view);
04290   image_view=DestroyCacheView(image_view);
04291   if (status == MagickFalse)
04292     shade_image=DestroyImage(shade_image);
04293   return(shade_image);
04294 }
04295 
04296 /*
04297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04298 %                                                                             %
04299 %                                                                             %
04300 %                                                                             %
04301 %     S h a r p e n I m a g e                                                 %
04302 %                                                                             %
04303 %                                                                             %
04304 %                                                                             %
04305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04306 %
04307 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
04308 %  operator of the given radius and standard deviation (sigma).  For
04309 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
04310 %  and SharpenImage() selects a suitable radius for you.
04311 %
04312 %  Using a separable kernel would be faster, but the negative weights cancel
04313 %  out on the corners of the kernel producing often undesirable ringing in the
04314 %  filtered result; this can be avoided by using a 2D gaussian shaped image
04315 %  sharpening kernel instead.
04316 %
04317 %  The format of the SharpenImage method is:
04318 %
04319 %    Image *SharpenImage(const Image *image,const double radius,
04320 %      const double sigma,ExceptionInfo *exception)
04321 %    Image *SharpenImageChannel(const Image *image,const ChannelType channel,
04322 %      const double radius,const double sigma,ExceptionInfo *exception)
04323 %
04324 %  A description of each parameter follows:
04325 %
04326 %    o image: the image.
04327 %
04328 %    o channel: the channel type.
04329 %
04330 %    o radius: the radius of the Gaussian, in pixels, not counting the center
04331 %      pixel.
04332 %
04333 %    o sigma: the standard deviation of the Laplacian, in pixels.
04334 %
04335 %    o exception: return any errors or warnings in this structure.
04336 %
04337 */
04338 
04339 MagickExport Image *SharpenImage(const Image *image,const double radius,
04340   const double sigma,ExceptionInfo *exception)
04341 {
04342   Image
04343     *sharp_image;
04344 
04345   sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
04346   return(sharp_image);
04347 }
04348 
04349 MagickExport Image *SharpenImageChannel(const Image *image,
04350   const ChannelType channel,const double radius,const double sigma,
04351   ExceptionInfo *exception)
04352 {
04353   double
04354     *kernel;
04355 
04356   Image
04357     *sharp_image;
04358 
04359   MagickRealType
04360     alpha,
04361     normalize;
04362 
04363   register long
04364     i,
04365     u,
04366     v;
04367 
04368   unsigned long
04369     width;
04370 
04371   assert(image != (const Image *) NULL);
04372   assert(image->signature == MagickSignature);
04373   if (image->debug != MagickFalse)
04374     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
04375   assert(exception != (ExceptionInfo *) NULL);
04376   assert(exception->signature == MagickSignature);
04377   width=GetOptimalKernelWidth2D(radius,sigma);
04378   kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
04379   if (kernel == (double *) NULL)
04380     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
04381   i=0;
04382   normalize=0.0;
04383   for (v=(-((long) width/2)); v <= (long) (width/2); v++)
04384   {
04385     for (u=(-((long) width/2)); u <= (long) (width/2); u++)
04386     {
04387       alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
04388       kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
04389       normalize+=kernel[i];
04390       i++;
04391     }
04392   }
04393   kernel[i/2]=(double) ((-2.0)*normalize);
04394   sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
04395   kernel=(double *) RelinquishMagickMemory(kernel);
04396   return(sharp_image);
04397 }
04398 
04399 /*
04400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04401 %                                                                             %
04402 %                                                                             %
04403