Powered by Pair ImageMagick logo
Image Magick
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | File List | Namespace Members | Data Fields | Globals | Related Pages

ImageMagick-6.1.1/magick/effect.c

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

Generated on Mon Oct 25 13:42:13 2004 for ImageMagick by doxygen 1.3.7
ImageMagick Copyright © 2004, ImageMagick Studio LLC