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-2008 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "magick/studio.h"
00044 #include "magick/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/resample.h"
00074 #include "magick/resample-private.h"
00075 #include "magick/resize.h"
00076 #include "magick/resource_.h"
00077 #include "magick/segment.h"
00078 #include "magick/shear.h"
00079 #include "magick/signature-private.h"
00080 #include "magick/string_.h"
00081 #include "magick/transform.h"
00082 #include "magick/threshold.h"
00083 
00084 /*
00085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00086 %                                                                             %
00087 %                                                                             %
00088 %                                                                             %
00089 %     A d a p t i v e B l u r I m a g e                                       %
00090 %                                                                             %
00091 %                                                                             %
00092 %                                                                             %
00093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00094 %
00095 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
00096 %  intensely near image edges and more intensely far from edges.  We blur the
00097 %  image with a Gaussian operator of the given radius and standard deviation
00098 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00099 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
00100 %
00101 %  The format of the AdaptiveBlurImage method is:
00102 %
00103 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
00104 %        const double sigma,ExceptionInfo *exception)
00105 %      Image *AdaptiveBlurImageChannel(const Image *image,
00106 %        const ChannelType channel,double radius,const double sigma,
00107 %        ExceptionInfo *exception)
00108 %
00109 %  A description of each parameter follows:
00110 %
00111 %    o image: the image.
00112 %
00113 %    o channel: the channel type.
00114 %
00115 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00116 %      pixel.
00117 %
00118 %    o sigma: the standard deviation of the Laplacian, in pixels.
00119 %
00120 %    o exception: return any errors or warnings in this structure.
00121 %
00122 */
00123 
00124 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
00125   const double sigma,ExceptionInfo *exception)
00126 {
00127   Image
00128     *blur_image;
00129 
00130   blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
00131     exception);
00132   return(blur_image);
00133 }
00134 
00135 MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
00136   const ChannelType channel,const double radius,const double sigma,
00137   ExceptionInfo *exception)
00138 {
00139 #define AdaptiveBlurImageTag  "Convolve/Image"
00140 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00141 
00142   double
00143     **kernel;
00144 
00145   Image
00146     *blur_image,
00147     *edge_image,
00148     *gaussian_image;
00149 
00150   long
00151     j,
00152     progress,
00153     y;
00154 
00155   MagickBooleanType
00156     status;
00157 
00158   MagickPixelPacket
00159     zero;
00160 
00161   MagickRealType
00162     alpha,
00163     normalize;
00164 
00165   register long
00166     i,
00167     u,
00168     v;
00169 
00170   unsigned long
00171     width;
00172 
00173   ViewInfo
00174     **blur_view,
00175     **edge_view,
00176     **image_view;
00177 
00178   assert(image != (const Image *) NULL);
00179   assert(image->signature == MagickSignature);
00180   if (image->debug != MagickFalse)
00181     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00182   assert(exception != (ExceptionInfo *) NULL);
00183   assert(exception->signature == MagickSignature);
00184   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00185   if (blur_image == (Image *) NULL)
00186     return((Image *) NULL);
00187   if (fabs(sigma) <= MagickEpsilon)
00188     return(blur_image);
00189   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00190     {
00191       InheritException(exception,&blur_image->exception);
00192       blur_image=DestroyImage(blur_image);
00193       return((Image *) NULL);
00194     }
00195   /*
00196     Edge detect the image brighness channel, level, blur, and level again.
00197   */
00198   edge_image=EdgeImage(image,radius,exception);
00199   if (edge_image == (Image *) NULL)
00200     {
00201       blur_image=DestroyImage(blur_image);
00202       return((Image *) NULL);
00203     }
00204   (void) LevelImage(edge_image,"20%,95%");
00205   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00206   if (gaussian_image != (Image *) NULL)
00207     {
00208       edge_image=DestroyImage(edge_image);
00209       edge_image=gaussian_image;
00210     }
00211   (void) LevelImage(edge_image,"10%,95%");
00212   /*
00213     Create a set of kernels from maximum (radius,sigma) to minimum.
00214   */
00215   width=GetOptimalKernelWidth2D(radius,sigma);
00216   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00217   if (kernel == (double **) NULL)
00218     {
00219       edge_image=DestroyImage(edge_image);
00220       blur_image=DestroyImage(blur_image);
00221       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00222     }
00223   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00224   for (i=0; i < (long) width; i+=2)
00225   {
00226     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00227       sizeof(**kernel));
00228     if (kernel[i] == (double *) NULL)
00229       break;
00230     j=0;
00231     normalize=0.0;
00232     for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00233     {
00234       for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00235       {
00236         alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00237         kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00238         if (((width-i) < 3) || (u != 0) || (v != 0))
00239           normalize+=kernel[i][j];
00240         j++;
00241       }
00242     }
00243     kernel[i][j/2]=(double) ((-2.0)*normalize);
00244     normalize=0.0;
00245     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00246       normalize+=kernel[i][j];
00247     if (fabs(normalize) <= MagickEpsilon)
00248       normalize=1.0;
00249     normalize=1.0/normalize;
00250     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00251       kernel[i][j]=(double) (normalize*kernel[i][j]);
00252   }
00253   if (i < (long) width)
00254     {
00255       for (i-=2; i >= 0; i-=2)
00256         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00257       kernel=(double **) RelinquishMagickMemory(kernel);
00258       edge_image=DestroyImage(edge_image);
00259       blur_image=DestroyImage(blur_image);
00260       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00261     }
00262   /*
00263     Adaptively blur image.
00264   */
00265   status=MagickTrue;
00266   progress=0;
00267   GetMagickPixelPacket(image,&zero);
00268   image_view=AcquireCacheViewThreadSet(image);
00269   edge_view=AcquireCacheViewThreadSet(edge_image);
00270   blur_view=AcquireCacheViewThreadSet(blur_image);
00271 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00272   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00273 #endif
00274   for (y=0; y < (long) blur_image->rows; y++)
00275   {
00276     register const IndexPacket
00277       *indexes;
00278 
00279     register const PixelPacket
00280       *p,
00281       *r;
00282 
00283     register IndexPacket
00284       *blur_indexes;
00285 
00286     register long
00287       id,
00288       x;
00289 
00290     register PixelPacket
00291       *q;
00292 
00293     if (status == MagickFalse)
00294       continue;
00295     id=GetCacheViewThreadId();
00296     r=GetCacheViewVirtualPixels(edge_view[id],0,y,edge_image->columns,1,
00297       exception);
00298     q=QueueCacheViewAuthenticPixels(blur_view[id],0,y,blur_image->columns,1,
00299       exception);
00300     if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00301       {
00302         status=MagickFalse;
00303         continue;
00304       }
00305     blur_indexes=GetCacheViewAuthenticIndexes(blur_view[id]);
00306     for (x=0; x < (long) blur_image->columns; x++)
00307     {
00308       MagickPixelPacket
00309         pixel;
00310 
00311       MagickRealType
00312         alpha,
00313         gamma;
00314 
00315       register const double
00316         *k;
00317 
00318       register long
00319         i,
00320         u,
00321         v;
00322 
00323       gamma=0.0;
00324       i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
00325       if (i < 0)
00326         i=0;
00327       else
00328         if (i > (long) width)
00329           i=(long) width;
00330       if ((i & 0x01) != 0)
00331         i--;
00332       p=GetCacheViewVirtualPixels(image_view[id],x-((long) (width-i)/2L),y-
00333         (long) ((width-i)/2L),width-i,width-i,exception);
00334       if (p == (const PixelPacket *) NULL)
00335         break;
00336       indexes=GetCacheViewVirtualIndexes(image_view[id]);
00337       pixel=zero;
00338       k=kernel[i];
00339       for (v=0; v < (long) (width-i); v++)
00340       {
00341         for (u=0; u < (long) (width-i); u++)
00342         {
00343           alpha=1.0;
00344           if (((channel & OpacityChannel) != 0) &&
00345               (image->matte != MagickFalse))
00346             alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00347           if ((channel & RedChannel) != 0)
00348             pixel.red+=(*k)*alpha*p->red;
00349           if ((channel & GreenChannel) != 0)
00350             pixel.green+=(*k)*alpha*p->green;
00351           if ((channel & BlueChannel) != 0)
00352             pixel.blue+=(*k)*alpha*p->blue;
00353           if ((channel & OpacityChannel) != 0)
00354             pixel.opacity+=(*k)*p->opacity;
00355           if (((channel & IndexChannel) != 0) &&
00356               (image->colorspace == CMYKColorspace))
00357             pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00358           gamma+=(*k)*alpha;
00359           k++;
00360           p++;
00361         }
00362       }
00363       gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00364       if ((channel & RedChannel) != 0)
00365         q->red=RoundToQuantum(gamma*pixel.red+image->bias);
00366       if ((channel & GreenChannel) != 0)
00367         q->green=RoundToQuantum(gamma*pixel.green+image->bias);
00368       if ((channel & BlueChannel) != 0)
00369         q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
00370       if ((channel & OpacityChannel) != 0)
00371         q->opacity=RoundToQuantum(pixel.opacity+image->bias);
00372       if (((channel & IndexChannel) != 0) &&
00373           (image->colorspace == CMYKColorspace))
00374         blur_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
00375       q++;
00376       r++;
00377     }
00378     if (SyncCacheViewAuthenticPixels(blur_view[id],exception) == MagickFalse)
00379       status=MagickFalse;
00380     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00381       {
00382         MagickBooleanType
00383           proceed;
00384 
00385 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00386   #pragma omp critical
00387 #endif
00388         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
00389           image->rows);
00390         if (proceed == MagickFalse)
00391           status=MagickFalse;
00392       }
00393   }
00394   blur_image->type=image->type;
00395   blur_view=DestroyCacheViewThreadSet(blur_view);
00396   edge_view=DestroyCacheViewThreadSet(edge_view);
00397   image_view=DestroyCacheViewThreadSet(image_view);
00398   edge_image=DestroyImage(edge_image);
00399   for (i=0; i < (long) width;  i+=2)
00400     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00401   kernel=(double **) RelinquishMagickMemory(kernel);
00402   if (status == MagickFalse)
00403     blur_image=DestroyImage(blur_image);
00404   return(blur_image);
00405 }
00406 
00407 /*
00408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00409 %                                                                             %
00410 %                                                                             %
00411 %                                                                             %
00412 %     A d a p t i v e S h a r p e n I m a g e                                 %
00413 %                                                                             %
00414 %                                                                             %
00415 %                                                                             %
00416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00417 %
00418 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
00419 %  intensely near image edges and less intensely far from edges. We sharpen the
00420 %  image with a Gaussian operator of the given radius and standard deviation
00421 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00422 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
00423 %
00424 %  The format of the AdaptiveSharpenImage method is:
00425 %
00426 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
00427 %        const double sigma,ExceptionInfo *exception)
00428 %      Image *AdaptiveSharpenImageChannel(const Image *image,
00429 %        const ChannelType channel,double radius,const double sigma,
00430 %        ExceptionInfo *exception)
00431 %
00432 %  A description of each parameter follows:
00433 %
00434 %    o image: the image.
00435 %
00436 %    o channel: the channel type.
00437 %
00438 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00439 %      pixel.
00440 %
00441 %    o sigma: the standard deviation of the Laplacian, in pixels.
00442 %
00443 %    o exception: return any errors or warnings in this structure.
00444 %
00445 */
00446 
00447 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
00448   const double sigma,ExceptionInfo *exception)
00449 {
00450   Image
00451     *sharp_image;
00452 
00453   sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
00454     exception);
00455   return(sharp_image);
00456 }
00457 
00458 MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
00459   const ChannelType channel,const double radius,const double sigma,
00460   ExceptionInfo *exception)
00461 {
00462 #define AdaptiveSharpenImageTag  "Convolve/Image"
00463 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00464 
00465   double
00466     **kernel;
00467 
00468   Image
00469     *sharp_image,
00470     *edge_image,
00471     *gaussian_image;
00472 
00473   long
00474     j,
00475     progress,
00476     y;
00477 
00478   MagickBooleanType
00479     status;
00480 
00481   MagickPixelPacket
00482     zero;
00483 
00484   MagickRealType
00485     alpha,
00486     normalize;
00487 
00488   register long
00489     i,
00490     u,
00491     v;
00492 
00493   unsigned long
00494     width;
00495 
00496   ViewInfo
00497     **sharp_view,
00498     **edge_view,
00499     **image_view;
00500 
00501   assert(image != (const Image *) NULL);
00502   assert(image->signature == MagickSignature);
00503   if (image->debug != MagickFalse)
00504     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00505   assert(exception != (ExceptionInfo *) NULL);
00506   assert(exception->signature == MagickSignature);
00507   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
00508   if (sharp_image == (Image *) NULL)
00509     return((Image *) NULL);
00510   if (fabs(sigma) <= MagickEpsilon)
00511     return(sharp_image);
00512   if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
00513     {
00514       InheritException(exception,&sharp_image->exception);
00515       sharp_image=DestroyImage(sharp_image);
00516       return((Image *) NULL);
00517     }
00518   /*
00519     Edge detect the image brighness channel, level, sharp, and level again.
00520   */
00521   edge_image=EdgeImage(image,radius,exception);
00522   if (edge_image == (Image *) NULL)
00523     {
00524       sharp_image=DestroyImage(sharp_image);
00525       return((Image *) NULL);
00526     }
00527   (void) LevelImage(edge_image,"20%,95%");
00528   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00529   if (gaussian_image != (Image *) NULL)
00530     {
00531       edge_image=DestroyImage(edge_image);
00532       edge_image=gaussian_image;
00533     }
00534   (void) LevelImage(edge_image,"10%,95%");
00535   /*
00536     Create a set of kernels from maximum (radius,sigma) to minimum.
00537   */
00538   width=GetOptimalKernelWidth2D(radius,sigma);
00539   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00540   if (kernel == (double **) NULL)
00541     {
00542       edge_image=DestroyImage(edge_image);
00543       sharp_image=DestroyImage(sharp_image);
00544       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00545     }
00546   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00547   for (i=0; i < (long) width; i+=2)
00548   {
00549     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
00550       sizeof(**kernel));
00551     if (kernel[i] == (double *) NULL)
00552       break;
00553     j=0;
00554     normalize=0.0;
00555     for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
00556     {
00557       for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
00558       {
00559         alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
00560         kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
00561         if (((width-i) < 3) || (u != 0) || (v != 0))
00562           normalize+=kernel[i][j];
00563         j++;
00564       }
00565     }
00566     kernel[i][j/2]=(double) ((-2.0)*normalize);
00567     normalize=0.0;
00568     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00569       normalize+=kernel[i][j];
00570     if (fabs(normalize) <= MagickEpsilon)
00571       normalize=1.0;
00572     normalize=1.0/normalize;
00573     for (j=0; j < (long) ((width-i)*(width-i)); j++)
00574       kernel[i][j]=(double) (normalize*kernel[i][j]);
00575   }
00576   if (i < (long) width)
00577     {
00578       for (i-=2; i >= 0; i-=2)
00579         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00580       kernel=(double **) RelinquishMagickMemory(kernel);
00581       edge_image=DestroyImage(edge_image);
00582       sharp_image=DestroyImage(sharp_image);
00583       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00584     }
00585   /*
00586     Adaptively sharpen image.
00587   */
00588   status=MagickTrue;
00589   progress=0;
00590   GetMagickPixelPacket(image,&zero);
00591   image_view=AcquireCacheViewThreadSet(image);
00592   edge_view=AcquireCacheViewThreadSet(edge_image);
00593   sharp_view=AcquireCacheViewThreadSet(sharp_image);
00594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00595   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00596 #endif
00597   for (y=0; y < (long) sharp_image->rows; y++)
00598   {
00599     register const IndexPacket
00600       *indexes;
00601 
00602     register const PixelPacket
00603       *p,
00604       *r;
00605 
00606     register IndexPacket
00607       *sharp_indexes;
00608 
00609     register long
00610       id,
00611       x;
00612 
00613     register PixelPacket
00614       *q;
00615 
00616     if (status == MagickFalse)
00617       continue;
00618     id=GetCacheViewThreadId();
00619     r=GetCacheViewVirtualPixels(edge_view[id],0,y,edge_image->columns,1,
00620       exception);
00621     q=QueueCacheViewAuthenticPixels(sharp_view[id],0,y,sharp_image->columns,1,
00622       exception);
00623     if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00624       {
00625         status=MagickFalse;
00626         continue;
00627       }
00628     sharp_indexes=GetCacheViewAuthenticIndexes(sharp_view[id]);
00629     for (x=0; x < (long) sharp_image->columns; x++)
00630     {
00631       MagickPixelPacket
00632         pixel;
00633 
00634       MagickRealType
00635         alpha,
00636         gamma;
00637 
00638       register const double
00639         *k;
00640 
00641       register long
00642         i,
00643         u,
00644         v;
00645 
00646       gamma=0.0;
00647       i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
00648       if (i < 0)
00649         i=0;
00650       else
00651         if (i > (long) width)
00652           i=(long) width;
00653       if ((i & 0x01) != 0)
00654         i--;
00655       p=GetCacheViewVirtualPixels(image_view[id],x-((long) (width-i)/2L),y-
00656         (long) ((width-i)/2L),width-i,width-i,exception);
00657       if (p == (const PixelPacket *) NULL)
00658         break;
00659       indexes=GetCacheViewVirtualIndexes(image_view[id]);
00660       k=kernel[i];
00661       pixel=zero;
00662       for (v=0; v < (long) (width-i); v++)
00663       {
00664         for (u=0; u < (long) (width-i); u++)
00665         {
00666           alpha=1.0;
00667           if (((channel & OpacityChannel) != 0) &&
00668               (image->matte != MagickFalse))
00669             alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00670           if ((channel & RedChannel) != 0)
00671             pixel.red+=(*k)*alpha*p->red;
00672           if ((channel & GreenChannel) != 0)
00673             pixel.green+=(*k)*alpha*p->green;
00674           if ((channel & BlueChannel) != 0)
00675             pixel.blue+=(*k)*alpha*p->blue;
00676           if ((channel & OpacityChannel) != 0)
00677             pixel.opacity+=(*k)*p->opacity;
00678           if (((channel & IndexChannel) != 0) &&
00679               (image->colorspace == CMYKColorspace))
00680             pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
00681           gamma+=(*k)*alpha;
00682           k++;
00683           p++;
00684         }
00685       }
00686       gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00687       if ((channel & RedChannel) != 0)
00688         q->red=RoundToQuantum(gamma*pixel.red+image->bias);
00689       if ((channel & GreenChannel) != 0)
00690         q->green=RoundToQuantum(gamma*pixel.green+image->bias);
00691       if ((channel & BlueChannel) != 0)
00692         q->blue=RoundToQuantum(gamma*pixel.blue+image->bias);
00693       if ((channel & OpacityChannel) != 0)
00694         q->opacity=RoundToQuantum(pixel.opacity+image->bias);
00695       if (((channel & IndexChannel) != 0) &&
00696           (image->colorspace == CMYKColorspace))
00697         sharp_indexes[x]=RoundToQuantum(gamma*pixel.index+image->bias);
00698       q++;
00699       r++;
00700     }
00701     if (SyncCacheViewAuthenticPixels(sharp_view[id],exception) == MagickFalse)
00702       status=MagickFalse;
00703     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00704       {
00705         MagickBooleanType
00706           proceed;
00707 
00708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00709   #pragma omp critical
00710 #endif
00711         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
00712           image->rows);
00713         if (proceed == MagickFalse)
00714           status=MagickFalse;
00715       }
00716   }
00717   sharp_image->type=image->type;
00718   sharp_view=DestroyCacheViewThreadSet(sharp_view);
00719   edge_view=DestroyCacheViewThreadSet(edge_view);
00720   image_view=DestroyCacheViewThreadSet(image_view);
00721   edge_image=DestroyImage(edge_image);
00722   for (i=0; i < (long) width;  i+=2)
00723     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
00724   kernel=(double **) RelinquishMagickMemory(kernel);
00725   if (status == MagickFalse)
00726     sharp_image=DestroyImage(sharp_image);
00727   return(sharp_image);
00728 }
00729 
00730 /*
00731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00732 %                                                                             %
00733 %                                                                             %
00734 %                                                                             %
00735 %     B l u r I m a g e                                                       %
00736 %                                                                             %
00737 %                                                                             %
00738 %                                                                             %
00739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00740 %
00741 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
00742 %  of the given radius and standard deviation (sigma).  For reasonable results,
00743 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
00744 %  selects a suitable radius for you.
00745 %
00746 %  BlurImage() differs from GaussianBlurImage() in that it uses a separable
00747 %  kernel which is faster but mathematically equivalent to the non-separable
00748 %  kernel.
00749 %
00750 %  The format of the BlurImage method is:
00751 %
00752 %      Image *BlurImage(const Image *image,const double radius,
00753 %        const double sigma,ExceptionInfo *exception)
00754 %      Image *BlurImageChannel(const Image *image,const ChannelType channel,
00755 %        const double radius,const double sigma,ExceptionInfo *exception)
00756 %
00757 %  A description of each parameter follows:
00758 %
00759 %    o image: the image.
00760 %
00761 %    o channel: the channel type.
00762 %
00763 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00764 %      pixel.
00765 %
00766 %    o sigma: the standard deviation of the Gaussian, in pixels.
00767 %
00768 %    o exception: return any errors or warnings in this structure.
00769 %
00770 */
00771 
00772 MagickExport Image *BlurImage(const Image *image,const double radius,
00773   const double sigma,ExceptionInfo *exception)
00774 {
00775   Image
00776     *blur_image;
00777 
00778   blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
00779   return(blur_image);
00780 }
00781 
00782 static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
00783 {
00784 #define KernelRank 3
00785 
00786   double
00787     *kernel;
00788 
00789   long
00790     bias;
00791 
00792   MagickRealType
00793     alpha,
00794     normalize;
00795 
00796   register long
00797     i;
00798 
00799   /*
00800     Generate a 1-D convolution kernel.
00801   */
00802   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00803   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
00804   if (kernel == (double *) NULL)
00805     return(0);
00806   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00807   bias=KernelRank*(long) width/2;
00808   for (i=(-bias); i <= bias; i++)
00809   {
00810     alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
00811       MagickSigma*MagickSigma)));
00812     kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
00813   }
00814   normalize=0.0;
00815   for (i=0; i < (long) width; i++)
00816     normalize+=kernel[i];
00817   for (i=0; i < (long) width; i++)
00818     kernel[i]/=normalize;
00819   return(kernel);
00820 }
00821 
00822 MagickExport Image *BlurImageChannel(const Image *image,
00823   const ChannelType channel,const double radius,const double sigma,
00824   ExceptionInfo *exception)
00825 {
00826 #define BlurImageTag  "Blur/Image"
00827 
00828   double
00829     *kernel;
00830 
00831   Image
00832     *blur_image;
00833 
00834   long
00835     progress,
00836     x,
00837     y;
00838 
00839   MagickBooleanType
00840     status;
00841 
00842   MagickPixelPacket
00843     zero;
00844 
00845   MagickRealType
00846     bias;
00847 
00848   register long
00849     i;
00850 
00851   unsigned long
00852     width;
00853 
00854   ViewInfo
00855     **blur_view,
00856     **image_view;
00857 
00858   /*
00859     Initialize blur image attributes.
00860   */
00861   assert(image != (Image *) NULL);
00862   assert(image->signature == MagickSignature);
00863   if (image->debug != MagickFalse)
00864     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00865   assert(exception != (ExceptionInfo *) NULL);
00866   assert(exception->signature == MagickSignature);
00867   blur_image=CloneImage(image,0,0,MagickTrue,exception);
00868   if (blur_image == (Image *) NULL)
00869     return((Image *) NULL);
00870   if (fabs(sigma) <= MagickEpsilon)
00871     return(blur_image);
00872   if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
00873     {
00874       InheritException(exception,&blur_image->exception);
00875       blur_image=DestroyImage(blur_image);
00876       return((Image *) NULL);
00877     }
00878   width=GetOptimalKernelWidth1D(radius,sigma);
00879   kernel=GetBlurKernel(width,sigma);
00880   if (kernel == (double *) NULL)
00881     {
00882       blur_image=DestroyImage(blur_image);
00883       return((Image *) NULL);
00884       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00885     }
00886   if (image->debug != MagickFalse)
00887     {
00888       char
00889         format[MaxTextExtent],
00890         *message;
00891 
00892       register const double
00893         *k;
00894 
00895       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00896         "  BlurImage with %ld kernel:",width);
00897       message=AcquireString("");
00898       k=kernel;
00899       for (i=0; i < (long) width; i++)
00900       {
00901         *message='\0';
00902         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
00903         (void) ConcatenateString(&message,format);
00904         (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
00905         (void) ConcatenateString(&message,format);
00906         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00907       }
00908       message=DestroyString(message);
00909     }
00910   /*
00911     Blur rows.
00912   */
00913   status=MagickTrue;
00914   progress=0;
00915   GetMagickPixelPacket(image,&zero);
00916   bias=image->bias;
00917   image_view=AcquireCacheViewThreadSet(image);
00918   blur_view=AcquireCacheViewThreadSet(blur_image);
00919 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00920   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00921 #endif
00922   for (y=0; y < (long) blur_image->rows; y++)
00923   {
00924     register const IndexPacket
00925       *indexes;
00926 
00927     register const PixelPacket
00928       *p;
00929 
00930     register IndexPacket
00931       *blur_indexes;
00932 
00933     register long
00934       id,
00935       x;
00936 
00937     register PixelPacket
00938       *q;
00939 
00940     if (status == MagickFalse)
00941       continue;
00942     id=GetCacheViewThreadId();
00943     p=GetCacheViewVirtualPixels(image_view[id],-((long) width/2L),y,
00944       image->columns+width,1,exception);
00945     q=GetCacheViewAuthenticPixels(blur_view[id],0,y,blur_image->columns,1,
00946       exception);
00947     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00948       {
00949         status=MagickFalse;
00950         continue;
00951       }
00952     indexes=GetCacheViewVirtualIndexes(image_view[id]);
00953     blur_indexes=GetCacheViewAuthenticIndexes(blur_view[id]);
00954     for (x=0; x < (long) blur_image->columns; x++)
00955     {
00956       MagickPixelPacket
00957         pixel;
00958 
00959       register const double
00960         *k;
00961 
00962       register long
00963         i;
00964 
00965       pixel=zero;
00966       k=kernel;
00967       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00968         {
00969           for (i=0; i < (long) width; i++)
00970           {
00971             pixel.red+=(*k)*(p+i)->red;
00972             pixel.green+=(*k)*(p+i)->green;
00973             pixel.blue+=(*k)*(p+i)->blue;
00974             pixel.opacity+=(*k)*(p+i)->opacity;
00975             k++;
00976           }
00977           if ((channel & RedChannel) != 0)
00978             q->red=RoundToQuantum(pixel.red+bias);
00979           if ((channel & GreenChannel) != 0)
00980             q->green=RoundToQuantum(pixel.green+bias);
00981           if ((channel & BlueChannel) != 0)
00982             q->blue=RoundToQuantum(pixel.blue+bias);
00983           if ((channel & OpacityChannel) != 0)
00984             q->opacity=RoundToQuantum(pixel.opacity+bias);
00985           if (((channel & IndexChannel) != 0) &&
00986               (image->colorspace == CMYKColorspace))
00987             {
00988               k=kernel;
00989               for (i=0; i < (long) width; i++)
00990               {
00991                 pixel.index+=(*k)*indexes[x+i];
00992                 k++;
00993               }
00994               blur_indexes[x]=RoundToQuantum(pixel.index+bias);
00995             }
00996         }
00997       else
00998         {
00999           MagickRealType
01000             alpha,
01001             gamma;
01002 
01003           gamma=0.0;
01004           for (i=0; i < (long) width; i++)
01005           {
01006             alpha=(MagickRealType) (QuantumScale*(QuantumRange-(p+i)->opacity));
01007             pixel.red+=(*k)*alpha*(p+i)->red;
01008             pixel.green+=(*k)*alpha*(p+i)->green;
01009             pixel.blue+=(*k)*alpha*(p+i)->blue;
01010             pixel.opacity+=(*k)*(p+i)->opacity;
01011             gamma+=(*k)*alpha;
01012             k++;
01013           }
01014           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01015           if ((channel & RedChannel) != 0)
01016             q->red=RoundToQuantum(gamma*pixel.red+bias);
01017           if ((channel & GreenChannel) != 0)
01018             q->green=RoundToQuantum(gamma*pixel.green+bias);
01019           if ((channel & BlueChannel) != 0)
01020             q->blue=RoundToQuantum(gamma*pixel.blue+bias);
01021           if ((channel & OpacityChannel) != 0)
01022             q->opacity=RoundToQuantum(pixel.opacity+bias);
01023           if (((channel & IndexChannel) != 0) &&
01024               (image->colorspace == CMYKColorspace))
01025             {
01026               k=kernel;
01027               for (i=0; i < (long) width; i++)
01028               {
01029                 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01030                   (p+i)->opacity));
01031                 pixel.index+=(*k)*alpha*indexes[x+i];
01032                 k++;
01033               }
01034               blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
01035             }
01036         }
01037       p++;
01038       q++;
01039     }
01040     if (SyncCacheViewAuthenticPixels(blur_view[id],exception) == MagickFalse)
01041       status=MagickFalse;
01042     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01043       {
01044         MagickBooleanType
01045           proceed;
01046 
01047 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01048   #pragma omp critical
01049 #endif
01050         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01051           blur_image->columns);
01052         if (proceed == MagickFalse)
01053           status=MagickFalse;
01054       }
01055   }
01056   /*
01057     Blur columns.
01058   */
01059   image_view=DestroyCacheViewThreadSet(image_view);
01060   image_view=AcquireCacheViewThreadSet(blur_image);
01061 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01062   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01063 #endif
01064   for (x=0; x < (long) blur_image->columns; x++)
01065   {
01066     register const IndexPacket
01067       *indexes;
01068 
01069     register const PixelPacket
01070       *p;
01071 
01072     register IndexPacket
01073       *blur_indexes;
01074 
01075     register long
01076       id,
01077       y;
01078 
01079     register PixelPacket
01080       *q;
01081 
01082     if (status == MagickFalse)
01083       continue;
01084     id=GetCacheViewThreadId();
01085     p=GetCacheViewVirtualPixels(image_view[id],x,-((long) width/2L),1,
01086       image->rows+width,exception);
01087     q=GetCacheVi