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