blob: 8ac984b3b22dadfd1c5482992dacae2e848d25c5 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
7% E F F E C T %
8% EEE FFF FFF EEE C T %
9% E F F E C T %
10% EEEEE F F EEEEE CCCC T %
11% %
12% %
13% MagickCore Image Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/accelerate.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/enhance.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000059#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/geometry.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/memory_.h"
65#include "MagickCore/monitor.h"
66#include "MagickCore/monitor-private.h"
67#include "MagickCore/montage.h"
68#include "MagickCore/morphology.h"
69#include "MagickCore/paint.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/property.h"
72#include "MagickCore/quantize.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/quantum-private.h"
75#include "MagickCore/random_.h"
76#include "MagickCore/random-private.h"
77#include "MagickCore/resample.h"
78#include "MagickCore/resample-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/segment.h"
82#include "MagickCore/shear.h"
83#include "MagickCore/signature-private.h"
84#include "MagickCore/string_.h"
85#include "MagickCore/thread-private.h"
86#include "MagickCore/transform.h"
87#include "MagickCore/threshold.h"
cristy3ed852e2009-09-05 21:47:34 +000088
89/*
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91% %
92% %
93% %
94% A d a p t i v e B l u r I m a g e %
95% %
96% %
97% %
98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99%
100% AdaptiveBlurImage() adaptively blurs the image by blurring less
101% intensely near image edges and more intensely far from edges. We blur the
102% image with a Gaussian operator of the given radius and standard deviation
103% (sigma). For reasonable results, radius should be larger than sigma. Use a
104% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
105%
106% The format of the AdaptiveBlurImage method is:
107%
108% Image *AdaptiveBlurImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000109% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000110%
111% A description of each parameter follows:
112%
113% o image: the image.
114%
cristy3ed852e2009-09-05 21:47:34 +0000115% o radius: the radius of the Gaussian, in pixels, not counting the center
116% pixel.
117%
118% o sigma: the standard deviation of the Laplacian, in pixels.
119%
cristy4c11c2b2011-09-05 20:17:07 +0000120% o bias: the bias.
121%
cristy3ed852e2009-09-05 21:47:34 +0000122% o exception: return any errors or warnings in this structure.
123%
124*/
125
cristyf89cb1d2011-07-07 01:24:37 +0000126MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
cristy051718b2011-08-28 22:49:25 +0000127 const char *levels,ExceptionInfo *exception)
cristyf89cb1d2011-07-07 01:24:37 +0000128{
129 double
130 black_point,
131 gamma,
132 white_point;
133
134 GeometryInfo
135 geometry_info;
136
137 MagickBooleanType
138 status;
139
140 MagickStatusType
141 flags;
142
143 /*
144 Parse levels.
145 */
146 if (levels == (char *) NULL)
147 return(MagickFalse);
148 flags=ParseGeometry(levels,&geometry_info);
149 black_point=geometry_info.rho;
150 white_point=(double) QuantumRange;
151 if ((flags & SigmaValue) != 0)
152 white_point=geometry_info.sigma;
153 gamma=1.0;
154 if ((flags & XiValue) != 0)
155 gamma=geometry_info.xi;
156 if ((flags & PercentValue) != 0)
157 {
158 black_point*=(double) image->columns*image->rows/100.0;
159 white_point*=(double) image->columns*image->rows/100.0;
160 }
161 if ((flags & SigmaValue) == 0)
162 white_point=(double) QuantumRange-black_point;
163 if ((flags & AspectValue ) == 0)
cristy7c0a0a42011-08-23 17:57:25 +0000164 status=LevelImage(image,black_point,white_point,gamma,exception);
cristyf89cb1d2011-07-07 01:24:37 +0000165 else
cristy7c0a0a42011-08-23 17:57:25 +0000166 status=LevelizeImage(image,black_point,white_point,gamma,exception);
cristyf89cb1d2011-07-07 01:24:37 +0000167 return(status);
168}
169
cristyf4ad9df2011-07-08 16:49:03 +0000170MagickExport Image *AdaptiveBlurImage(const Image *image,
cristy4c11c2b2011-09-05 20:17:07 +0000171 const double radius,const double sigma,const double bias,
172 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000173{
174#define AdaptiveBlurImageTag "Convolve/Image"
175#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
176
cristyc4c8d132010-01-07 01:58:38 +0000177 CacheView
178 *blur_view,
179 *edge_view,
180 *image_view;
181
cristy3ed852e2009-09-05 21:47:34 +0000182 double
cristy47e00502009-12-17 19:19:57 +0000183 **kernel,
184 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000185
186 Image
187 *blur_image,
188 *edge_image,
189 *gaussian_image;
190
cristy3ed852e2009-09-05 21:47:34 +0000191 MagickBooleanType
192 status;
193
cristybb503372010-05-27 20:51:26 +0000194 MagickOffsetType
195 progress;
196
cristybb503372010-05-27 20:51:26 +0000197 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000198 i;
cristy3ed852e2009-09-05 21:47:34 +0000199
cristybb503372010-05-27 20:51:26 +0000200 size_t
cristy3ed852e2009-09-05 21:47:34 +0000201 width;
202
cristybb503372010-05-27 20:51:26 +0000203 ssize_t
204 j,
205 k,
206 u,
207 v,
208 y;
209
cristy3ed852e2009-09-05 21:47:34 +0000210 assert(image != (const Image *) NULL);
211 assert(image->signature == MagickSignature);
212 if (image->debug != MagickFalse)
213 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
214 assert(exception != (ExceptionInfo *) NULL);
215 assert(exception->signature == MagickSignature);
216 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
217 if (blur_image == (Image *) NULL)
218 return((Image *) NULL);
219 if (fabs(sigma) <= MagickEpsilon)
220 return(blur_image);
cristy574cc262011-08-05 01:23:58 +0000221 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000222 {
cristy3ed852e2009-09-05 21:47:34 +0000223 blur_image=DestroyImage(blur_image);
224 return((Image *) NULL);
225 }
226 /*
227 Edge detect the image brighness channel, level, blur, and level again.
228 */
cristy8ae632d2011-09-05 17:29:53 +0000229 edge_image=EdgeImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000230 if (edge_image == (Image *) NULL)
231 {
232 blur_image=DestroyImage(blur_image);
233 return((Image *) NULL);
234 }
cristy051718b2011-08-28 22:49:25 +0000235 (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000236 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
237 if (gaussian_image != (Image *) NULL)
238 {
239 edge_image=DestroyImage(edge_image);
240 edge_image=gaussian_image;
241 }
cristy051718b2011-08-28 22:49:25 +0000242 (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000243 /*
244 Create a set of kernels from maximum (radius,sigma) to minimum.
245 */
246 width=GetOptimalKernelWidth2D(radius,sigma);
247 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
248 if (kernel == (double **) NULL)
249 {
250 edge_image=DestroyImage(edge_image);
251 blur_image=DestroyImage(blur_image);
252 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
253 }
254 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000255 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000256 {
257 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
258 sizeof(**kernel));
259 if (kernel[i] == (double *) NULL)
260 break;
cristy47e00502009-12-17 19:19:57 +0000261 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000262 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000263 k=0;
264 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000265 {
cristy47e00502009-12-17 19:19:57 +0000266 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
cristy4205a3c2010-09-12 20:19:59 +0000268 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
269 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000270 normalize+=kernel[i][k];
271 k++;
cristy3ed852e2009-09-05 21:47:34 +0000272 }
273 }
cristy3ed852e2009-09-05 21:47:34 +0000274 if (fabs(normalize) <= MagickEpsilon)
275 normalize=1.0;
276 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000277 for (k=0; k < (j*j); k++)
278 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000279 }
cristybb503372010-05-27 20:51:26 +0000280 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000281 {
282 for (i-=2; i >= 0; i-=2)
283 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
284 kernel=(double **) RelinquishMagickMemory(kernel);
285 edge_image=DestroyImage(edge_image);
286 blur_image=DestroyImage(blur_image);
287 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
288 }
289 /*
290 Adaptively blur image.
291 */
292 status=MagickTrue;
293 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000294 image_view=AcquireCacheView(image);
295 edge_view=AcquireCacheView(edge_image);
296 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000297#if defined(MAGICKCORE_OPENMP_SUPPORT)
298 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000299#endif
cristybb503372010-05-27 20:51:26 +0000300 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000301 {
cristy4c08aed2011-07-01 19:47:50 +0000302 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000303 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000304
cristy4c08aed2011-07-01 19:47:50 +0000305 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000306 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000307
cristy117ff172010-08-15 21:35:32 +0000308 register ssize_t
309 x;
310
cristy3ed852e2009-09-05 21:47:34 +0000311 if (status == MagickFalse)
312 continue;
313 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
314 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
315 exception);
cristyacd2ed22011-08-30 01:44:23 +0000316 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000317 {
318 status=MagickFalse;
319 continue;
320 }
cristybb503372010-05-27 20:51:26 +0000321 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000322 {
cristy4c11c2b2011-09-05 20:17:07 +0000323 register const Quantum
324 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000325
cristybb503372010-05-27 20:51:26 +0000326 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000327 i;
cristy3ed852e2009-09-05 21:47:34 +0000328
cristy4c11c2b2011-09-05 20:17:07 +0000329 ssize_t
330 center,
331 j;
332
333 j=(ssize_t) ceil((double) width*QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +0000334 GetPixelIntensity(edge_image,r)-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000335 if (j < 0)
336 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000337 else
cristy4c11c2b2011-09-05 20:17:07 +0000338 if (j > (ssize_t) width)
339 j=(ssize_t) width;
340 if ((j & 0x01) != 0)
341 j--;
342 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
343 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000344 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000345 break;
cristy4c11c2b2011-09-05 20:17:07 +0000346 center=(ssize_t) GetPixelChannels(image)*(width-j)*
347 ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2);
348 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000349 {
cristy4c11c2b2011-09-05 20:17:07 +0000350 MagickRealType
351 alpha,
352 gamma,
353 pixel;
354
355 PixelChannel
356 channel;
357
358 PixelTrait
359 blur_traits,
360 traits;
361
362 register const double
363 *restrict k;
364
365 register const Quantum
366 *restrict pixels;
367
368 register ssize_t
369 u;
370
371 ssize_t
372 v;
373
374 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
375 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
376 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
377 if ((traits == UndefinedPixelTrait) ||
378 (blur_traits == UndefinedPixelTrait))
379 continue;
380 if ((blur_traits & CopyPixelTrait) != 0)
381 {
382 q[channel]=p[center+i];
383 continue;
384 }
385 k=kernel[j];
386 pixels=p;
387 pixel=bias;
388 gamma=0.0;
389 if ((blur_traits & BlendPixelTrait) == 0)
390 {
391 /*
392 No alpha blending.
393 */
394 for (v=0; v < (ssize_t) (width-j); v++)
395 {
396 for (u=0; u < (ssize_t) (width-j); u++)
397 {
398 pixel+=(*k)*pixels[i];
399 gamma+=(*k);
400 k++;
401 pixels+=GetPixelChannels(image);
402 }
403 }
404 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
405 q[channel]=ClampToQuantum(gamma*pixel);
406 continue;
407 }
408 /*
409 Alpha blending.
410 */
411 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000412 {
cristy4c11c2b2011-09-05 20:17:07 +0000413 for (u=0; u < (ssize_t) (width-j); u++)
414 {
415 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
416 pixel+=(*k)*alpha*pixels[i];
417 gamma+=(*k)*alpha;
418 k++;
419 pixels+=GetPixelChannels(image);
420 }
cristy3ed852e2009-09-05 21:47:34 +0000421 }
cristy4c11c2b2011-09-05 20:17:07 +0000422 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
423 q[channel]=ClampToQuantum(gamma*pixel);
cristy3ed852e2009-09-05 21:47:34 +0000424 }
cristyed231572011-07-14 02:18:59 +0000425 q+=GetPixelChannels(blur_image);
426 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000427 }
428 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
429 status=MagickFalse;
430 if (image->progress_monitor != (MagickProgressMonitor) NULL)
431 {
432 MagickBooleanType
433 proceed;
434
cristyb5d5f722009-11-04 03:03:49 +0000435#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy8ae632d2011-09-05 17:29:53 +0000436 #pragma omp critical (MagickCore_AdaptiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +0000437#endif
438 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
439 image->rows);
440 if (proceed == MagickFalse)
441 status=MagickFalse;
442 }
443 }
444 blur_image->type=image->type;
445 blur_view=DestroyCacheView(blur_view);
446 edge_view=DestroyCacheView(edge_view);
447 image_view=DestroyCacheView(image_view);
448 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000449 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000450 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
451 kernel=(double **) RelinquishMagickMemory(kernel);
452 if (status == MagickFalse)
453 blur_image=DestroyImage(blur_image);
454 return(blur_image);
455}
456
457/*
458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459% %
460% %
461% %
462% A d a p t i v e S h a r p e n I m a g e %
463% %
464% %
465% %
466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467%
468% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
469% intensely near image edges and less intensely far from edges. We sharpen the
470% image with a Gaussian operator of the given radius and standard deviation
471% (sigma). For reasonable results, radius should be larger than sigma. Use a
472% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
473%
474% The format of the AdaptiveSharpenImage method is:
475%
476% Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000477% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000478%
479% A description of each parameter follows:
480%
481% o image: the image.
482%
cristy3ed852e2009-09-05 21:47:34 +0000483% o radius: the radius of the Gaussian, in pixels, not counting the center
484% pixel.
485%
486% o sigma: the standard deviation of the Laplacian, in pixels.
487%
cristy4c11c2b2011-09-05 20:17:07 +0000488% o bias: the bias.
489%
cristy3ed852e2009-09-05 21:47:34 +0000490% o exception: return any errors or warnings in this structure.
491%
492*/
cristy3ed852e2009-09-05 21:47:34 +0000493MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000494 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000495{
cristy3ed852e2009-09-05 21:47:34 +0000496#define AdaptiveSharpenImageTag "Convolve/Image"
497#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
498
cristyc4c8d132010-01-07 01:58:38 +0000499 CacheView
500 *sharp_view,
501 *edge_view,
502 *image_view;
503
cristy3ed852e2009-09-05 21:47:34 +0000504 double
cristy47e00502009-12-17 19:19:57 +0000505 **kernel,
506 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000507
508 Image
509 *sharp_image,
510 *edge_image,
511 *gaussian_image;
512
cristy3ed852e2009-09-05 21:47:34 +0000513 MagickBooleanType
514 status;
515
cristybb503372010-05-27 20:51:26 +0000516 MagickOffsetType
517 progress;
518
cristybb503372010-05-27 20:51:26 +0000519 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000520 i;
cristy3ed852e2009-09-05 21:47:34 +0000521
cristybb503372010-05-27 20:51:26 +0000522 size_t
cristy3ed852e2009-09-05 21:47:34 +0000523 width;
524
cristybb503372010-05-27 20:51:26 +0000525 ssize_t
526 j,
527 k,
528 u,
529 v,
530 y;
531
cristy3ed852e2009-09-05 21:47:34 +0000532 assert(image != (const Image *) NULL);
533 assert(image->signature == MagickSignature);
534 if (image->debug != MagickFalse)
535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
536 assert(exception != (ExceptionInfo *) NULL);
537 assert(exception->signature == MagickSignature);
538 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
539 if (sharp_image == (Image *) NULL)
540 return((Image *) NULL);
541 if (fabs(sigma) <= MagickEpsilon)
542 return(sharp_image);
cristy574cc262011-08-05 01:23:58 +0000543 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000544 {
cristy3ed852e2009-09-05 21:47:34 +0000545 sharp_image=DestroyImage(sharp_image);
546 return((Image *) NULL);
547 }
548 /*
549 Edge detect the image brighness channel, level, sharp, and level again.
550 */
cristy8ae632d2011-09-05 17:29:53 +0000551 edge_image=EdgeImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000552 if (edge_image == (Image *) NULL)
553 {
554 sharp_image=DestroyImage(sharp_image);
555 return((Image *) NULL);
556 }
cristy051718b2011-08-28 22:49:25 +0000557 (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000558 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
559 if (gaussian_image != (Image *) NULL)
560 {
561 edge_image=DestroyImage(edge_image);
562 edge_image=gaussian_image;
563 }
cristy051718b2011-08-28 22:49:25 +0000564 (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000565 /*
566 Create a set of kernels from maximum (radius,sigma) to minimum.
567 */
568 width=GetOptimalKernelWidth2D(radius,sigma);
569 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
570 if (kernel == (double **) NULL)
571 {
572 edge_image=DestroyImage(edge_image);
573 sharp_image=DestroyImage(sharp_image);
574 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575 }
576 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000577 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000578 {
579 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
580 sizeof(**kernel));
581 if (kernel[i] == (double *) NULL)
582 break;
cristy47e00502009-12-17 19:19:57 +0000583 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000584 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000585 k=0;
586 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000587 {
cristy47e00502009-12-17 19:19:57 +0000588 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000589 {
cristy4205a3c2010-09-12 20:19:59 +0000590 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
591 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000592 normalize+=kernel[i][k];
593 k++;
cristy3ed852e2009-09-05 21:47:34 +0000594 }
595 }
cristy3ed852e2009-09-05 21:47:34 +0000596 if (fabs(normalize) <= MagickEpsilon)
597 normalize=1.0;
598 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000599 for (k=0; k < (j*j); k++)
600 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000601 }
cristybb503372010-05-27 20:51:26 +0000602 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000603 {
604 for (i-=2; i >= 0; i-=2)
605 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
606 kernel=(double **) RelinquishMagickMemory(kernel);
607 edge_image=DestroyImage(edge_image);
608 sharp_image=DestroyImage(sharp_image);
609 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
610 }
611 /*
612 Adaptively sharpen image.
613 */
614 status=MagickTrue;
615 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000616 image_view=AcquireCacheView(image);
617 edge_view=AcquireCacheView(edge_image);
618 sharp_view=AcquireCacheView(sharp_image);
cristyb5d5f722009-11-04 03:03:49 +0000619#if defined(MAGICKCORE_OPENMP_SUPPORT)
620 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000621#endif
cristybb503372010-05-27 20:51:26 +0000622 for (y=0; y < (ssize_t) sharp_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000623 {
cristy4c08aed2011-07-01 19:47:50 +0000624 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000625 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000626
cristy4c08aed2011-07-01 19:47:50 +0000627 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000628 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000629
cristy117ff172010-08-15 21:35:32 +0000630 register ssize_t
631 x;
632
cristy3ed852e2009-09-05 21:47:34 +0000633 if (status == MagickFalse)
634 continue;
635 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
636 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
637 exception);
cristy4c08aed2011-07-01 19:47:50 +0000638 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000639 {
640 status=MagickFalse;
641 continue;
642 }
cristybb503372010-05-27 20:51:26 +0000643 for (x=0; x < (ssize_t) sharp_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000644 {
cristy4c11c2b2011-09-05 20:17:07 +0000645 register const Quantum
646 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000647
cristybb503372010-05-27 20:51:26 +0000648 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000649 i;
cristy3ed852e2009-09-05 21:47:34 +0000650
cristy4c11c2b2011-09-05 20:17:07 +0000651 ssize_t
652 center,
653 j;
654
655 j=(ssize_t) ceil((double) width*QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +0000656 GetPixelIntensity(edge_image,r)-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000657 if (j < 0)
658 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000659 else
cristy4c11c2b2011-09-05 20:17:07 +0000660 if (j > (ssize_t) width)
661 j=(ssize_t) width;
662 if ((j & 0x01) != 0)
663 j--;
664 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
665 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000666 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000667 break;
cristy4c11c2b2011-09-05 20:17:07 +0000668 center=(ssize_t) GetPixelChannels(image)*(width-j)*
669 ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2);
670 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000671 {
cristy4c11c2b2011-09-05 20:17:07 +0000672 MagickRealType
673 alpha,
674 gamma,
675 pixel;
676
677 PixelChannel
678 channel;
679
680 PixelTrait
681 sharp_traits,
682 traits;
683
684 register const double
685 *restrict k;
686
687 register const Quantum
688 *restrict pixels;
689
690 register ssize_t
691 u;
692
693 ssize_t
694 v;
695
696 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
697 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
698 sharp_traits=GetPixelChannelMapTraits(sharp_image,channel);
699 if ((traits == UndefinedPixelTrait) ||
700 (sharp_traits == UndefinedPixelTrait))
701 continue;
702 if ((sharp_traits & CopyPixelTrait) != 0)
703 {
704 q[channel]=p[center+i];
705 continue;
706 }
707 k=kernel[j];
708 pixels=p;
709 pixel=bias;
710 gamma=0.0;
711 if ((sharp_traits & BlendPixelTrait) == 0)
712 {
713 /*
714 No alpha blending.
715 */
716 for (v=0; v < (ssize_t) (width-j); v++)
717 {
718 for (u=0; u < (ssize_t) (width-j); u++)
719 {
720 pixel+=(*k)*pixels[i];
721 gamma+=(*k);
722 k++;
723 pixels+=GetPixelChannels(image);
724 }
725 }
726 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
727 q[channel]=ClampToQuantum(gamma*pixel);
728 continue;
729 }
730 /*
731 Alpha blending.
732 */
733 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
cristy4c11c2b2011-09-05 20:17:07 +0000735 for (u=0; u < (ssize_t) (width-j); u++)
736 {
737 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
738 pixel+=(*k)*alpha*pixels[i];
739 gamma+=(*k)*alpha;
740 k++;
741 pixels+=GetPixelChannels(image);
742 }
cristy3ed852e2009-09-05 21:47:34 +0000743 }
cristy4c11c2b2011-09-05 20:17:07 +0000744 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
745 q[channel]=ClampToQuantum(gamma*pixel);
cristy3ed852e2009-09-05 21:47:34 +0000746 }
cristyed231572011-07-14 02:18:59 +0000747 q+=GetPixelChannels(sharp_image);
748 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000749 }
750 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
751 status=MagickFalse;
752 if (image->progress_monitor != (MagickProgressMonitor) NULL)
753 {
754 MagickBooleanType
755 proceed;
756
cristyb5d5f722009-11-04 03:03:49 +0000757#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000758 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000759#endif
760 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
761 image->rows);
762 if (proceed == MagickFalse)
763 status=MagickFalse;
764 }
765 }
766 sharp_image->type=image->type;
767 sharp_view=DestroyCacheView(sharp_view);
768 edge_view=DestroyCacheView(edge_view);
769 image_view=DestroyCacheView(image_view);
770 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000771 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000772 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
773 kernel=(double **) RelinquishMagickMemory(kernel);
774 if (status == MagickFalse)
775 sharp_image=DestroyImage(sharp_image);
776 return(sharp_image);
777}
778
779/*
780%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781% %
782% %
783% %
784% B l u r I m a g e %
785% %
786% %
787% %
788%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789%
790% BlurImage() blurs an image. We convolve the image with a Gaussian operator
791% of the given radius and standard deviation (sigma). For reasonable results,
792% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
793% selects a suitable radius for you.
794%
795% BlurImage() differs from GaussianBlurImage() in that it uses a separable
796% kernel which is faster but mathematically equivalent to the non-separable
797% kernel.
798%
799% The format of the BlurImage method is:
800%
801% Image *BlurImage(const Image *image,const double radius,
802% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000803%
804% A description of each parameter follows:
805%
806% o image: the image.
807%
cristy3ed852e2009-09-05 21:47:34 +0000808% o radius: the radius of the Gaussian, in pixels, not counting the center
809% pixel.
810%
811% o sigma: the standard deviation of the Gaussian, in pixels.
812%
813% o exception: return any errors or warnings in this structure.
814%
815*/
816
cristybb503372010-05-27 20:51:26 +0000817static double *GetBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000818{
cristy3ed852e2009-09-05 21:47:34 +0000819 double
cristy47e00502009-12-17 19:19:57 +0000820 *kernel,
821 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000822
cristy117ff172010-08-15 21:35:32 +0000823 register ssize_t
824 i;
825
cristybb503372010-05-27 20:51:26 +0000826 ssize_t
cristy47e00502009-12-17 19:19:57 +0000827 j,
828 k;
cristy3ed852e2009-09-05 21:47:34 +0000829
cristy3ed852e2009-09-05 21:47:34 +0000830 /*
831 Generate a 1-D convolution kernel.
832 */
833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
834 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
835 if (kernel == (double *) NULL)
836 return(0);
cristy3ed852e2009-09-05 21:47:34 +0000837 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000838 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000839 i=0;
840 for (k=(-j); k <= j; k++)
841 {
cristy4205a3c2010-09-12 20:19:59 +0000842 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
843 (MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +0000844 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +0000845 i++;
846 }
cristybb503372010-05-27 20:51:26 +0000847 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000848 kernel[i]/=normalize;
849 return(kernel);
850}
851
cristyf4ad9df2011-07-08 16:49:03 +0000852MagickExport Image *BlurImage(const Image *image,const double radius,
853 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000854{
855#define BlurImageTag "Blur/Image"
856
cristyc4c8d132010-01-07 01:58:38 +0000857 CacheView
858 *blur_view,
859 *image_view;
860
cristy3ed852e2009-09-05 21:47:34 +0000861 double
862 *kernel;
863
864 Image
865 *blur_image;
866
cristy3ed852e2009-09-05 21:47:34 +0000867 MagickBooleanType
868 status;
869
cristybb503372010-05-27 20:51:26 +0000870 MagickOffsetType
871 progress;
872
cristy4c08aed2011-07-01 19:47:50 +0000873 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000874 bias;
875
cristybb503372010-05-27 20:51:26 +0000876 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000877 i;
878
cristybb503372010-05-27 20:51:26 +0000879 size_t
cristy3ed852e2009-09-05 21:47:34 +0000880 width;
881
cristybb503372010-05-27 20:51:26 +0000882 ssize_t
883 x,
884 y;
885
cristy3ed852e2009-09-05 21:47:34 +0000886 /*
887 Initialize blur image attributes.
888 */
889 assert(image != (Image *) NULL);
890 assert(image->signature == MagickSignature);
891 if (image->debug != MagickFalse)
892 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
893 assert(exception != (ExceptionInfo *) NULL);
894 assert(exception->signature == MagickSignature);
895 blur_image=CloneImage(image,0,0,MagickTrue,exception);
896 if (blur_image == (Image *) NULL)
897 return((Image *) NULL);
898 if (fabs(sigma) <= MagickEpsilon)
899 return(blur_image);
cristy574cc262011-08-05 01:23:58 +0000900 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000901 {
cristy3ed852e2009-09-05 21:47:34 +0000902 blur_image=DestroyImage(blur_image);
903 return((Image *) NULL);
904 }
905 width=GetOptimalKernelWidth1D(radius,sigma);
906 kernel=GetBlurKernel(width,sigma);
907 if (kernel == (double *) NULL)
908 {
909 blur_image=DestroyImage(blur_image);
910 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
911 }
912 if (image->debug != MagickFalse)
913 {
914 char
915 format[MaxTextExtent],
916 *message;
917
918 register const double
919 *k;
920
921 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000922 " BlurImage with %.20g kernel:",(double) width);
cristy3ed852e2009-09-05 21:47:34 +0000923 message=AcquireString("");
924 k=kernel;
cristybb503372010-05-27 20:51:26 +0000925 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000926 {
927 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000928 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
cristy3ed852e2009-09-05 21:47:34 +0000929 (void) ConcatenateString(&message,format);
cristyb51dff52011-05-19 16:55:47 +0000930 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristy3ed852e2009-09-05 21:47:34 +0000931 (void) ConcatenateString(&message,format);
932 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
933 }
934 message=DestroyString(message);
935 }
936 /*
937 Blur rows.
938 */
939 status=MagickTrue;
940 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000941 GetPixelInfo(image,&bias);
942 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000943 image_view=AcquireCacheView(image);
944 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000945#if defined(MAGICKCORE_OPENMP_SUPPORT)
946 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000947#endif
cristybb503372010-05-27 20:51:26 +0000948 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000949 {
cristy4c08aed2011-07-01 19:47:50 +0000950 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000951 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000952
cristy4c08aed2011-07-01 19:47:50 +0000953 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000954 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000955
cristy117ff172010-08-15 21:35:32 +0000956 register ssize_t
957 x;
958
cristy3ed852e2009-09-05 21:47:34 +0000959 if (status == MagickFalse)
960 continue;
cristy117ff172010-08-15 21:35:32 +0000961 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
962 image->columns+width,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000963 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
964 exception);
cristy4c08aed2011-07-01 19:47:50 +0000965 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000966 {
967 status=MagickFalse;
968 continue;
969 }
cristybb503372010-05-27 20:51:26 +0000970 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000971 {
cristy4c08aed2011-07-01 19:47:50 +0000972 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000973 pixel;
974
975 register const double
cristyc47d1f82009-11-26 01:44:43 +0000976 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000977
cristy4c08aed2011-07-01 19:47:50 +0000978 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000979 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000980
cristybb503372010-05-27 20:51:26 +0000981 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000982 i;
983
cristyddd82202009-11-03 20:14:50 +0000984 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +0000985 k=kernel;
986 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +0000987 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +0000988 (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000989 {
cristybb503372010-05-27 20:51:26 +0000990 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000991 {
cristy4c08aed2011-07-01 19:47:50 +0000992 pixel.red+=(*k)*GetPixelRed(image,kernel_pixels);
993 pixel.green+=(*k)*GetPixelGreen(image,kernel_pixels);
994 pixel.blue+=(*k)*GetPixelBlue(image,kernel_pixels);
995 if (image->colorspace == CMYKColorspace)
996 pixel.black+=(*k)*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000997 k++;
cristyed231572011-07-14 02:18:59 +0000998 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000999 }
cristyed231572011-07-14 02:18:59 +00001000 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001001 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001002 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001003 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001004 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001005 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001006 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001007 (blur_image->colorspace == CMYKColorspace))
1008 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001009 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001010 {
1011 k=kernel;
1012 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001013 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001014 {
cristy4c08aed2011-07-01 19:47:50 +00001015 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001016 k++;
cristyed231572011-07-14 02:18:59 +00001017 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001018 }
cristy4c08aed2011-07-01 19:47:50 +00001019 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001020 }
1021 }
1022 else
1023 {
1024 MagickRealType
1025 alpha,
1026 gamma;
1027
1028 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00001029 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001030 {
cristy4c08aed2011-07-01 19:47:50 +00001031 alpha=(MagickRealType) (QuantumScale*
1032 GetPixelAlpha(image,kernel_pixels));
1033 pixel.red+=(*k)*alpha*GetPixelRed(image,kernel_pixels);
1034 pixel.green+=(*k)*alpha*GetPixelGreen(image,kernel_pixels);
1035 pixel.blue+=(*k)*alpha*GetPixelBlue(image,kernel_pixels);
1036 if (image->colorspace == CMYKColorspace)
1037 pixel.black+=(*k)*alpha*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001038 gamma+=(*k)*alpha;
1039 k++;
cristyed231572011-07-14 02:18:59 +00001040 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001041 }
1042 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00001043 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001044 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001045 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001046 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001047 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001048 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001049 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001050 (blur_image->colorspace == CMYKColorspace))
1051 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001052 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001053 {
1054 k=kernel;
1055 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001056 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001057 {
cristy4c08aed2011-07-01 19:47:50 +00001058 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001059 k++;
cristyed231572011-07-14 02:18:59 +00001060 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001061 }
cristy4c08aed2011-07-01 19:47:50 +00001062 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001063 }
1064 }
cristyed231572011-07-14 02:18:59 +00001065 p+=GetPixelChannels(image);
1066 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001067 }
1068 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1069 status=MagickFalse;
1070 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1071 {
1072 MagickBooleanType
1073 proceed;
1074
cristyb5d5f722009-11-04 03:03:49 +00001075#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001076 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001077#endif
1078 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1079 blur_image->columns);
1080 if (proceed == MagickFalse)
1081 status=MagickFalse;
1082 }
1083 }
1084 blur_view=DestroyCacheView(blur_view);
1085 image_view=DestroyCacheView(image_view);
1086 /*
1087 Blur columns.
1088 */
1089 image_view=AcquireCacheView(blur_image);
1090 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00001091#if defined(MAGICKCORE_OPENMP_SUPPORT)
1092 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001093#endif
cristybb503372010-05-27 20:51:26 +00001094 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001095 {
cristy4c08aed2011-07-01 19:47:50 +00001096 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001097 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001098
cristy4c08aed2011-07-01 19:47:50 +00001099 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001100 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001101
cristy117ff172010-08-15 21:35:32 +00001102 register ssize_t
1103 y;
1104
cristy3ed852e2009-09-05 21:47:34 +00001105 if (status == MagickFalse)
1106 continue;
cristy117ff172010-08-15 21:35:32 +00001107 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1108 image->rows+width,exception);
cristy3ed852e2009-09-05 21:47:34 +00001109 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00001110 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001111 {
1112 status=MagickFalse;
1113 continue;
1114 }
cristybb503372010-05-27 20:51:26 +00001115 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001116 {
cristy4c08aed2011-07-01 19:47:50 +00001117 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001118 pixel;
1119
1120 register const double
cristyc47d1f82009-11-26 01:44:43 +00001121 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00001122
cristy4c08aed2011-07-01 19:47:50 +00001123 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001124 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001125
cristybb503372010-05-27 20:51:26 +00001126 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001127 i;
1128
cristyddd82202009-11-03 20:14:50 +00001129 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00001130 k=kernel;
1131 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +00001132 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +00001133 (blur_image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001134 {
cristybb503372010-05-27 20:51:26 +00001135 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001136 {
cristy4c08aed2011-07-01 19:47:50 +00001137 pixel.red+=(*k)*GetPixelRed(blur_image,kernel_pixels);
1138 pixel.green+=(*k)*GetPixelGreen(blur_image,kernel_pixels);
1139 pixel.blue+=(*k)*GetPixelBlue(blur_image,kernel_pixels);
1140 if (blur_image->colorspace == CMYKColorspace)
1141 pixel.black+=(*k)*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001142 k++;
cristyed231572011-07-14 02:18:59 +00001143 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001144 }
cristyed231572011-07-14 02:18:59 +00001145 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001146 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001147 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001148 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001149 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001150 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001151 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001152 (blur_image->colorspace == CMYKColorspace))
1153 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001154 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001155 {
1156 k=kernel;
1157 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001158 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001159 {
cristy4c08aed2011-07-01 19:47:50 +00001160 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001161 k++;
cristyed231572011-07-14 02:18:59 +00001162 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001163 }
cristy4c08aed2011-07-01 19:47:50 +00001164 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001165 }
1166 }
1167 else
1168 {
1169 MagickRealType
1170 alpha,
1171 gamma;
1172
1173 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00001174 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001175 {
cristy46f08202010-01-10 04:04:21 +00001176 alpha=(MagickRealType) (QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +00001177 GetPixelAlpha(blur_image,kernel_pixels));
1178 pixel.red+=(*k)*alpha*GetPixelRed(blur_image,kernel_pixels);
1179 pixel.green+=(*k)*alpha*GetPixelGreen(blur_image,kernel_pixels);
1180 pixel.blue+=(*k)*alpha*GetPixelBlue(blur_image,kernel_pixels);
1181 if (blur_image->colorspace == CMYKColorspace)
1182 pixel.black+=(*k)*alpha*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001183 gamma+=(*k)*alpha;
1184 k++;
cristyed231572011-07-14 02:18:59 +00001185 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001186 }
1187 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00001188 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001189 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001190 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001191 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001192 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001193 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001194 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001195 (blur_image->colorspace == CMYKColorspace))
1196 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001197 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001198 {
1199 k=kernel;
1200 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001201 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001202 {
cristy4c08aed2011-07-01 19:47:50 +00001203 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001204 k++;
cristyed231572011-07-14 02:18:59 +00001205 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001206 }
cristy4c08aed2011-07-01 19:47:50 +00001207 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001208 }
1209 }
cristyed231572011-07-14 02:18:59 +00001210 p+=GetPixelChannels(blur_image);
1211 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001212 }
1213 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1214 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00001215 if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001216 {
1217 MagickBooleanType
1218 proceed;
1219
cristyb5d5f722009-11-04 03:03:49 +00001220#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001221 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001222#endif
cristy4c08aed2011-07-01 19:47:50 +00001223 proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1224 blur_image->rows+blur_image->columns);
cristy3ed852e2009-09-05 21:47:34 +00001225 if (proceed == MagickFalse)
1226 status=MagickFalse;
1227 }
1228 }
1229 blur_view=DestroyCacheView(blur_view);
1230 image_view=DestroyCacheView(image_view);
1231 kernel=(double *) RelinquishMagickMemory(kernel);
1232 if (status == MagickFalse)
1233 blur_image=DestroyImage(blur_image);
1234 blur_image->type=image->type;
1235 return(blur_image);
1236}
1237
1238/*
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240% %
1241% %
1242% %
cristyfccdab92009-11-30 16:43:57 +00001243% C o n v o l v e I m a g e %
1244% %
1245% %
1246% %
1247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248%
1249% ConvolveImage() applies a custom convolution kernel to the image.
1250%
1251% The format of the ConvolveImage method is:
1252%
cristy5e6be1e2011-07-16 01:23:39 +00001253% Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1254% ExceptionInfo *exception)
1255%
cristyfccdab92009-11-30 16:43:57 +00001256% A description of each parameter follows:
1257%
1258% o image: the image.
1259%
cristy5e6be1e2011-07-16 01:23:39 +00001260% o kernel: the filtering kernel.
cristyfccdab92009-11-30 16:43:57 +00001261%
1262% o exception: return any errors or warnings in this structure.
1263%
1264*/
cristy5e6be1e2011-07-16 01:23:39 +00001265MagickExport Image *ConvolveImage(const Image *image,
1266 const KernelInfo *kernel_info,ExceptionInfo *exception)
cristyfccdab92009-11-30 16:43:57 +00001267{
cristyfccdab92009-11-30 16:43:57 +00001268#define ConvolveImageTag "Convolve/Image"
1269
cristyc4c8d132010-01-07 01:58:38 +00001270 CacheView
1271 *convolve_view,
cristy105ba3c2011-07-18 02:28:38 +00001272 *image_view;
cristyc4c8d132010-01-07 01:58:38 +00001273
cristyfccdab92009-11-30 16:43:57 +00001274 Image
1275 *convolve_image;
1276
cristyfccdab92009-11-30 16:43:57 +00001277 MagickBooleanType
1278 status;
1279
cristybb503372010-05-27 20:51:26 +00001280 MagickOffsetType
1281 progress;
1282
cristybb503372010-05-27 20:51:26 +00001283 ssize_t
cristy574cc262011-08-05 01:23:58 +00001284 center,
cristybb503372010-05-27 20:51:26 +00001285 y;
1286
cristyfccdab92009-11-30 16:43:57 +00001287 /*
1288 Initialize convolve image attributes.
1289 */
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 if (image->debug != MagickFalse)
1293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1294 assert(exception != (ExceptionInfo *) NULL);
1295 assert(exception->signature == MagickSignature);
cristy5e6be1e2011-07-16 01:23:39 +00001296 if ((kernel_info->width % 2) == 0)
cristyfccdab92009-11-30 16:43:57 +00001297 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
cristy08429172011-07-14 17:18:16 +00001298 convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1299 exception);
cristyfccdab92009-11-30 16:43:57 +00001300 if (convolve_image == (Image *) NULL)
1301 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00001302 if (SetImageStorageClass(convolve_image,DirectClass,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001303 {
cristyfccdab92009-11-30 16:43:57 +00001304 convolve_image=DestroyImage(convolve_image);
1305 return((Image *) NULL);
1306 }
1307 if (image->debug != MagickFalse)
1308 {
1309 char
1310 format[MaxTextExtent],
1311 *message;
1312
cristy117ff172010-08-15 21:35:32 +00001313 register const double
1314 *k;
1315
cristy4e154852011-07-14 13:28:53 +00001316 register ssize_t
1317 u;
1318
cristybb503372010-05-27 20:51:26 +00001319 ssize_t
cristyfccdab92009-11-30 16:43:57 +00001320 v;
1321
cristyfccdab92009-11-30 16:43:57 +00001322 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristy5e6be1e2011-07-16 01:23:39 +00001323 " ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1324 (double) kernel_info->height);
cristyfccdab92009-11-30 16:43:57 +00001325 message=AcquireString("");
cristy5e6be1e2011-07-16 01:23:39 +00001326 k=kernel_info->values;
1327 for (v=0; v < (ssize_t) kernel_info->width; v++)
cristyfccdab92009-11-30 16:43:57 +00001328 {
1329 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00001330 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristyfccdab92009-11-30 16:43:57 +00001331 (void) ConcatenateString(&message,format);
cristy5e6be1e2011-07-16 01:23:39 +00001332 for (u=0; u < (ssize_t) kernel_info->height; u++)
cristyfccdab92009-11-30 16:43:57 +00001333 {
cristyb51dff52011-05-19 16:55:47 +00001334 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristyfccdab92009-11-30 16:43:57 +00001335 (void) ConcatenateString(&message,format);
1336 }
1337 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1338 }
1339 message=DestroyString(message);
1340 }
1341 /*
cristyfccdab92009-11-30 16:43:57 +00001342 Convolve image.
1343 */
cristy574cc262011-08-05 01:23:58 +00001344 center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
1345 (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2);
cristyfccdab92009-11-30 16:43:57 +00001346 status=MagickTrue;
1347 progress=0;
cristyfccdab92009-11-30 16:43:57 +00001348 image_view=AcquireCacheView(image);
1349 convolve_view=AcquireCacheView(convolve_image);
1350#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy175653e2011-07-10 23:13:34 +00001351 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristyfccdab92009-11-30 16:43:57 +00001352#endif
cristybb503372010-05-27 20:51:26 +00001353 for (y=0; y < (ssize_t) image->rows; y++)
cristyfccdab92009-11-30 16:43:57 +00001354 {
cristy4c08aed2011-07-01 19:47:50 +00001355 register const Quantum
cristy105ba3c2011-07-18 02:28:38 +00001356 *restrict p;
cristyfccdab92009-11-30 16:43:57 +00001357
cristy4c08aed2011-07-01 19:47:50 +00001358 register Quantum
cristyfccdab92009-11-30 16:43:57 +00001359 *restrict q;
1360
cristy117ff172010-08-15 21:35:32 +00001361 register ssize_t
1362 x;
1363
cristyfccdab92009-11-30 16:43:57 +00001364 if (status == MagickFalse)
1365 continue;
cristy105ba3c2011-07-18 02:28:38 +00001366 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1367 (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1368 kernel_info->height,exception);
cristy08429172011-07-14 17:18:16 +00001369 q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
cristyfccdab92009-11-30 16:43:57 +00001370 exception);
cristy105ba3c2011-07-18 02:28:38 +00001371 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristyfccdab92009-11-30 16:43:57 +00001372 {
1373 status=MagickFalse;
1374 continue;
1375 }
cristybb503372010-05-27 20:51:26 +00001376 for (x=0; x < (ssize_t) image->columns; x++)
cristyfccdab92009-11-30 16:43:57 +00001377 {
cristybb503372010-05-27 20:51:26 +00001378 register ssize_t
cristyed231572011-07-14 02:18:59 +00001379 i;
cristyfccdab92009-11-30 16:43:57 +00001380
cristya30d9ba2011-07-23 21:00:48 +00001381 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristyed231572011-07-14 02:18:59 +00001382 {
cristyed231572011-07-14 02:18:59 +00001383 MagickRealType
cristy4e154852011-07-14 13:28:53 +00001384 alpha,
1385 gamma,
cristyed231572011-07-14 02:18:59 +00001386 pixel;
1387
1388 PixelChannel
1389 channel;
1390
1391 PixelTrait
1392 convolve_traits,
1393 traits;
1394
1395 register const double
1396 *restrict k;
1397
1398 register const Quantum
cristyeb52cde2011-07-17 01:52:52 +00001399 *restrict pixels;
cristyed231572011-07-14 02:18:59 +00001400
1401 register ssize_t
1402 u;
1403
1404 ssize_t
1405 v;
1406
cristy30301712011-07-18 15:06:51 +00001407 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy30301712011-07-18 15:06:51 +00001408 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001409 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001410 if ((traits == UndefinedPixelTrait) ||
1411 (convolve_traits == UndefinedPixelTrait))
cristy4e154852011-07-14 13:28:53 +00001412 continue;
1413 if ((convolve_traits & CopyPixelTrait) != 0)
1414 {
cristyf7dc44c2011-07-20 14:41:15 +00001415 q[channel]=p[center+i];
cristy4e154852011-07-14 13:28:53 +00001416 continue;
1417 }
cristy5e6be1e2011-07-16 01:23:39 +00001418 k=kernel_info->values;
cristy105ba3c2011-07-18 02:28:38 +00001419 pixels=p;
cristy0a922382011-07-16 15:30:34 +00001420 pixel=kernel_info->bias;
cristy222b19c2011-08-04 01:35:11 +00001421 if ((convolve_traits & BlendPixelTrait) == 0)
cristyfccdab92009-11-30 16:43:57 +00001422 {
cristyed231572011-07-14 02:18:59 +00001423 /*
cristy4e154852011-07-14 13:28:53 +00001424 No alpha blending.
cristyed231572011-07-14 02:18:59 +00001425 */
cristyeb52cde2011-07-17 01:52:52 +00001426 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristyfccdab92009-11-30 16:43:57 +00001427 {
cristyeb52cde2011-07-17 01:52:52 +00001428 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy175653e2011-07-10 23:13:34 +00001429 {
cristyeb52cde2011-07-17 01:52:52 +00001430 pixel+=(*k)*pixels[i];
cristyed231572011-07-14 02:18:59 +00001431 k++;
cristya30d9ba2011-07-23 21:00:48 +00001432 pixels+=GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001433 }
cristya30d9ba2011-07-23 21:00:48 +00001434 pixels+=image->columns*GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001435 }
cristyf7dc44c2011-07-20 14:41:15 +00001436 q[channel]=ClampToQuantum(pixel);
cristy4e154852011-07-14 13:28:53 +00001437 continue;
cristyed231572011-07-14 02:18:59 +00001438 }
cristy4e154852011-07-14 13:28:53 +00001439 /*
1440 Alpha blending.
1441 */
1442 gamma=0.0;
cristyeb52cde2011-07-17 01:52:52 +00001443 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristy4e154852011-07-14 13:28:53 +00001444 {
cristyeb52cde2011-07-17 01:52:52 +00001445 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy4e154852011-07-14 13:28:53 +00001446 {
cristyeb52cde2011-07-17 01:52:52 +00001447 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1448 pixel+=(*k)*alpha*pixels[i];
cristy4e154852011-07-14 13:28:53 +00001449 gamma+=(*k)*alpha;
1450 k++;
cristya30d9ba2011-07-23 21:00:48 +00001451 pixels+=GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001452 }
cristya30d9ba2011-07-23 21:00:48 +00001453 pixels+=image->columns*GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001454 }
cristy1ce96d02011-07-14 17:57:24 +00001455 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristye7a41c92011-07-20 21:31:01 +00001456 q[channel]=ClampToQuantum(gamma*pixel);
cristyed231572011-07-14 02:18:59 +00001457 }
cristya30d9ba2011-07-23 21:00:48 +00001458 p+=GetPixelChannels(image);
1459 q+=GetPixelChannels(convolve_image);
cristyfccdab92009-11-30 16:43:57 +00001460 }
cristyed231572011-07-14 02:18:59 +00001461 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001462 status=MagickFalse;
1463 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1464 {
1465 MagickBooleanType
1466 proceed;
1467
1468#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001469 #pragma omp critical (MagickCore_ConvolveImage)
cristyfccdab92009-11-30 16:43:57 +00001470#endif
1471 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1472 if (proceed == MagickFalse)
1473 status=MagickFalse;
1474 }
1475 }
1476 convolve_image->type=image->type;
1477 convolve_view=DestroyCacheView(convolve_view);
1478 image_view=DestroyCacheView(image_view);
cristyfccdab92009-11-30 16:43:57 +00001479 if (status == MagickFalse)
1480 convolve_image=DestroyImage(convolve_image);
1481 return(convolve_image);
1482}
1483
1484/*
1485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486% %
1487% %
1488% %
cristy3ed852e2009-09-05 21:47:34 +00001489% D e s p e c k l e I m a g e %
1490% %
1491% %
1492% %
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494%
1495% DespeckleImage() reduces the speckle noise in an image while perserving the
1496% edges of the original image.
1497%
1498% The format of the DespeckleImage method is:
1499%
1500% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1501%
1502% A description of each parameter follows:
1503%
1504% o image: the image.
1505%
1506% o exception: return any errors or warnings in this structure.
1507%
1508*/
1509
cristybb503372010-05-27 20:51:26 +00001510static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1511 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
cristy3ed852e2009-09-05 21:47:34 +00001512 const int polarity)
1513{
cristy3ed852e2009-09-05 21:47:34 +00001514 MagickRealType
1515 v;
1516
cristy3ed852e2009-09-05 21:47:34 +00001517 register Quantum
1518 *p,
1519 *q,
1520 *r,
1521 *s;
1522
cristy117ff172010-08-15 21:35:32 +00001523 register ssize_t
1524 x;
1525
1526 ssize_t
1527 y;
1528
cristy3ed852e2009-09-05 21:47:34 +00001529 assert(f != (Quantum *) NULL);
1530 assert(g != (Quantum *) NULL);
1531 p=f+(columns+2);
1532 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001533 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1534 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001535 {
1536 p++;
1537 q++;
1538 r++;
1539 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001540 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001541 {
1542 v=(MagickRealType) (*p);
1543 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1544 v+=ScaleCharToQuantum(1);
1545 *q=(Quantum) v;
1546 p++;
1547 q++;
1548 r++;
1549 }
1550 else
cristybb503372010-05-27 20:51:26 +00001551 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001552 {
1553 v=(MagickRealType) (*p);
1554 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
cristybb503372010-05-27 20:51:26 +00001555 v-=(ssize_t) ScaleCharToQuantum(1);
cristy3ed852e2009-09-05 21:47:34 +00001556 *q=(Quantum) v;
1557 p++;
1558 q++;
1559 r++;
1560 }
1561 p++;
1562 q++;
1563 r++;
1564 }
1565 p=f+(columns+2);
1566 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001567 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1568 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1569 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001570 {
1571 p++;
1572 q++;
1573 r++;
1574 s++;
1575 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001576 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001577 {
1578 v=(MagickRealType) (*q);
1579 if (((MagickRealType) *s >=
1580 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1581 ((MagickRealType) *r > v))
1582 v+=ScaleCharToQuantum(1);
1583 *p=(Quantum) v;
1584 p++;
1585 q++;
1586 r++;
1587 s++;
1588 }
1589 else
cristybb503372010-05-27 20:51:26 +00001590 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001591 {
1592 v=(MagickRealType) (*q);
1593 if (((MagickRealType) *s <=
1594 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1595 ((MagickRealType) *r < v))
1596 v-=(MagickRealType) ScaleCharToQuantum(1);
1597 *p=(Quantum) v;
1598 p++;
1599 q++;
1600 r++;
1601 s++;
1602 }
1603 p++;
1604 q++;
1605 r++;
1606 s++;
1607 }
1608}
1609
1610MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1611{
1612#define DespeckleImageTag "Despeckle/Image"
1613
cristy2407fc22009-09-11 00:55:25 +00001614 CacheView
1615 *despeckle_view,
1616 *image_view;
1617
cristy3ed852e2009-09-05 21:47:34 +00001618 Image
1619 *despeckle_image;
1620
cristy3ed852e2009-09-05 21:47:34 +00001621 MagickBooleanType
1622 status;
1623
cristya58c3172011-02-19 19:23:11 +00001624 register ssize_t
1625 i;
1626
cristy3ed852e2009-09-05 21:47:34 +00001627 Quantum
cristy65b9f392011-02-22 14:22:54 +00001628 *restrict buffers,
1629 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +00001630
1631 size_t
cristya58c3172011-02-19 19:23:11 +00001632 length,
1633 number_channels;
cristy117ff172010-08-15 21:35:32 +00001634
cristybb503372010-05-27 20:51:26 +00001635 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001636 X[4] = {0, 1, 1,-1},
1637 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001638
cristy3ed852e2009-09-05 21:47:34 +00001639 /*
1640 Allocate despeckled image.
1641 */
1642 assert(image != (const Image *) NULL);
1643 assert(image->signature == MagickSignature);
1644 if (image->debug != MagickFalse)
1645 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1646 assert(exception != (ExceptionInfo *) NULL);
1647 assert(exception->signature == MagickSignature);
1648 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1649 exception);
1650 if (despeckle_image == (Image *) NULL)
1651 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00001652 if (SetImageStorageClass(despeckle_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001653 {
cristy3ed852e2009-09-05 21:47:34 +00001654 despeckle_image=DestroyImage(despeckle_image);
1655 return((Image *) NULL);
1656 }
1657 /*
1658 Allocate image buffers.
1659 */
1660 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy65b9f392011-02-22 14:22:54 +00001661 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1662 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1663 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001664 {
cristy65b9f392011-02-22 14:22:54 +00001665 if (buffers != (Quantum *) NULL)
1666 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1667 if (pixels != (Quantum *) NULL)
1668 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001669 despeckle_image=DestroyImage(despeckle_image);
1670 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1671 }
1672 /*
1673 Reduce speckle in the image.
1674 */
1675 status=MagickTrue;
cristy109695a2011-02-19 19:38:14 +00001676 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
cristy3ed852e2009-09-05 21:47:34 +00001677 image_view=AcquireCacheView(image);
1678 despeckle_view=AcquireCacheView(despeckle_image);
cristy8df3d002011-02-19 19:40:59 +00001679 for (i=0; i < (ssize_t) number_channels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001680 {
cristy3ed852e2009-09-05 21:47:34 +00001681 register Quantum
1682 *buffer,
1683 *pixel;
1684
cristyc1488b52011-02-19 18:54:15 +00001685 register ssize_t
cristya58c3172011-02-19 19:23:11 +00001686 k,
cristyc1488b52011-02-19 18:54:15 +00001687 x;
1688
cristy117ff172010-08-15 21:35:32 +00001689 ssize_t
1690 j,
1691 y;
1692
cristy3ed852e2009-09-05 21:47:34 +00001693 if (status == MagickFalse)
1694 continue;
cristy65b9f392011-02-22 14:22:54 +00001695 pixel=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001696 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
cristy65b9f392011-02-22 14:22:54 +00001697 buffer=buffers;
cristybb503372010-05-27 20:51:26 +00001698 j=(ssize_t) image->columns+2;
1699 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001700 {
cristy4c08aed2011-07-01 19:47:50 +00001701 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001702 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001703
1704 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001705 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001706 break;
1707 j++;
cristybb503372010-05-27 20:51:26 +00001708 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001709 {
cristya58c3172011-02-19 19:23:11 +00001710 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001711 {
cristy4c08aed2011-07-01 19:47:50 +00001712 case 0: pixel[j]=GetPixelRed(image,p); break;
1713 case 1: pixel[j]=GetPixelGreen(image,p); break;
1714 case 2: pixel[j]=GetPixelBlue(image,p); break;
1715 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1716 case 4: pixel[j]=GetPixelBlack(image,p); break;
cristy3ed852e2009-09-05 21:47:34 +00001717 default: break;
1718 }
cristyed231572011-07-14 02:18:59 +00001719 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001720 j++;
1721 }
1722 j++;
1723 }
cristy3ed852e2009-09-05 21:47:34 +00001724 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
cristya58c3172011-02-19 19:23:11 +00001725 for (k=0; k < 4; k++)
cristy3ed852e2009-09-05 21:47:34 +00001726 {
cristya58c3172011-02-19 19:23:11 +00001727 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1728 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1729 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1730 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
cristy3ed852e2009-09-05 21:47:34 +00001731 }
cristybb503372010-05-27 20:51:26 +00001732 j=(ssize_t) image->columns+2;
1733 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001734 {
1735 MagickBooleanType
1736 sync;
1737
cristy4c08aed2011-07-01 19:47:50 +00001738 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001739 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001740
1741 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1742 1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001743 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001744 break;
1745 j++;
cristybb503372010-05-27 20:51:26 +00001746 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001747 {
cristya58c3172011-02-19 19:23:11 +00001748 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001749 {
cristy4c08aed2011-07-01 19:47:50 +00001750 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1751 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1752 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1753 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1754 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
cristy3ed852e2009-09-05 21:47:34 +00001755 default: break;
1756 }
cristyed231572011-07-14 02:18:59 +00001757 q+=GetPixelChannels(despeckle_image);
cristy3ed852e2009-09-05 21:47:34 +00001758 j++;
1759 }
1760 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1761 if (sync == MagickFalse)
1762 {
1763 status=MagickFalse;
1764 break;
1765 }
1766 j++;
1767 }
1768 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1769 {
1770 MagickBooleanType
1771 proceed;
1772
cristya58c3172011-02-19 19:23:11 +00001773 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1774 number_channels);
cristy3ed852e2009-09-05 21:47:34 +00001775 if (proceed == MagickFalse)
1776 status=MagickFalse;
1777 }
1778 }
1779 despeckle_view=DestroyCacheView(despeckle_view);
1780 image_view=DestroyCacheView(image_view);
cristy65b9f392011-02-22 14:22:54 +00001781 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1782 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001783 despeckle_image->type=image->type;
1784 if (status == MagickFalse)
1785 despeckle_image=DestroyImage(despeckle_image);
1786 return(despeckle_image);
1787}
1788
1789/*
1790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1791% %
1792% %
1793% %
1794% E d g e I m a g e %
1795% %
1796% %
1797% %
1798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1799%
1800% EdgeImage() finds edges in an image. Radius defines the radius of the
1801% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1802% radius for you.
1803%
1804% The format of the EdgeImage method is:
1805%
1806% Image *EdgeImage(const Image *image,const double radius,
cristy8ae632d2011-09-05 17:29:53 +00001807% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001808%
1809% A description of each parameter follows:
1810%
1811% o image: the image.
1812%
1813% o radius: the radius of the pixel neighborhood.
1814%
cristy8ae632d2011-09-05 17:29:53 +00001815% o sigma: the standard deviation of the Gaussian, in pixels.
1816%
cristy3ed852e2009-09-05 21:47:34 +00001817% o exception: return any errors or warnings in this structure.
1818%
1819*/
1820MagickExport Image *EdgeImage(const Image *image,const double radius,
cristy8ae632d2011-09-05 17:29:53 +00001821 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001822{
1823 Image
1824 *edge_image;
1825
cristy41cbe682011-07-15 19:12:37 +00001826 KernelInfo
1827 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001828
cristybb503372010-05-27 20:51:26 +00001829 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001830 i;
1831
cristybb503372010-05-27 20:51:26 +00001832 size_t
cristy3ed852e2009-09-05 21:47:34 +00001833 width;
1834
cristy41cbe682011-07-15 19:12:37 +00001835 ssize_t
1836 j,
1837 u,
1838 v;
1839
cristy3ed852e2009-09-05 21:47:34 +00001840 assert(image != (const Image *) NULL);
1841 assert(image->signature == MagickSignature);
1842 if (image->debug != MagickFalse)
1843 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1844 assert(exception != (ExceptionInfo *) NULL);
1845 assert(exception->signature == MagickSignature);
cristy8ae632d2011-09-05 17:29:53 +00001846 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001847 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001848 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001849 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001850 kernel_info->width=width;
1851 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001852 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1853 kernel_info->width*sizeof(*kernel_info->values));
1854 if (kernel_info->values == (double *) NULL)
1855 {
1856 kernel_info=DestroyKernelInfo(kernel_info);
1857 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1858 }
1859 j=(ssize_t) kernel_info->width/2;
1860 i=0;
1861 for (v=(-j); v <= j; v++)
1862 {
1863 for (u=(-j); u <= j; u++)
1864 {
1865 kernel_info->values[i]=(-1.0);
1866 i++;
1867 }
1868 }
1869 kernel_info->values[i/2]=(double) (width*width-1.0);
cristy0a922382011-07-16 15:30:34 +00001870 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001871 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001872 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001873 return(edge_image);
1874}
1875
1876/*
1877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878% %
1879% %
1880% %
1881% E m b o s s I m a g e %
1882% %
1883% %
1884% %
1885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886%
1887% EmbossImage() returns a grayscale image with a three-dimensional effect.
1888% We convolve the image with a Gaussian operator of the given radius and
1889% standard deviation (sigma). For reasonable results, radius should be
1890% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1891% radius for you.
1892%
1893% The format of the EmbossImage method is:
1894%
1895% Image *EmbossImage(const Image *image,const double radius,
1896% const double sigma,ExceptionInfo *exception)
1897%
1898% A description of each parameter follows:
1899%
1900% o image: the image.
1901%
1902% o radius: the radius of the pixel neighborhood.
1903%
1904% o sigma: the standard deviation of the Gaussian, in pixels.
1905%
1906% o exception: return any errors or warnings in this structure.
1907%
1908*/
1909MagickExport Image *EmbossImage(const Image *image,const double radius,
1910 const double sigma,ExceptionInfo *exception)
1911{
cristy3ed852e2009-09-05 21:47:34 +00001912 Image
1913 *emboss_image;
1914
cristy41cbe682011-07-15 19:12:37 +00001915 KernelInfo
1916 *kernel_info;
1917
cristybb503372010-05-27 20:51:26 +00001918 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001919 i;
1920
cristybb503372010-05-27 20:51:26 +00001921 size_t
cristy3ed852e2009-09-05 21:47:34 +00001922 width;
1923
cristy117ff172010-08-15 21:35:32 +00001924 ssize_t
1925 j,
1926 k,
1927 u,
1928 v;
1929
cristy41cbe682011-07-15 19:12:37 +00001930 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001931 assert(image->signature == MagickSignature);
1932 if (image->debug != MagickFalse)
1933 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1934 assert(exception != (ExceptionInfo *) NULL);
1935 assert(exception->signature == MagickSignature);
1936 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001937 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001938 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001939 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001940 kernel_info->width=width;
1941 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001942 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1943 kernel_info->width*sizeof(*kernel_info->values));
1944 if (kernel_info->values == (double *) NULL)
1945 {
1946 kernel_info=DestroyKernelInfo(kernel_info);
1947 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1948 }
1949 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001950 k=j;
1951 i=0;
1952 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001953 {
cristy47e00502009-12-17 19:19:57 +00001954 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001955 {
cristy41cbe682011-07-15 19:12:37 +00001956 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001957 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001958 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001959 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001960 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001961 i++;
1962 }
cristy47e00502009-12-17 19:19:57 +00001963 k--;
cristy3ed852e2009-09-05 21:47:34 +00001964 }
cristy0a922382011-07-16 15:30:34 +00001965 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001966 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001967 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001968 if (emboss_image != (Image *) NULL)
cristy6d8c3d72011-08-22 01:20:01 +00001969 (void) EqualizeImage(emboss_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001970 return(emboss_image);
1971}
1972
1973/*
1974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1975% %
1976% %
1977% %
1978% G a u s s i a n B l u r I m a g e %
1979% %
1980% %
1981% %
1982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1983%
1984% GaussianBlurImage() blurs an image. We convolve the image with a
1985% Gaussian operator of the given radius and standard deviation (sigma).
1986% For reasonable results, the radius should be larger than sigma. Use a
1987% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1988%
1989% The format of the GaussianBlurImage method is:
1990%
1991% Image *GaussianBlurImage(const Image *image,onst double radius,
1992% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001993%
1994% A description of each parameter follows:
1995%
1996% o image: the image.
1997%
cristy3ed852e2009-09-05 21:47:34 +00001998% o radius: the radius of the Gaussian, in pixels, not counting the center
1999% pixel.
2000%
2001% o sigma: the standard deviation of the Gaussian, in pixels.
2002%
2003% o exception: return any errors or warnings in this structure.
2004%
2005*/
cristy41cbe682011-07-15 19:12:37 +00002006MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
2007 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002008{
cristy3ed852e2009-09-05 21:47:34 +00002009 Image
2010 *blur_image;
2011
cristy41cbe682011-07-15 19:12:37 +00002012 KernelInfo
2013 *kernel_info;
2014
cristybb503372010-05-27 20:51:26 +00002015 register ssize_t
cristy47e00502009-12-17 19:19:57 +00002016 i;
2017
cristybb503372010-05-27 20:51:26 +00002018 size_t
cristy3ed852e2009-09-05 21:47:34 +00002019 width;
2020
cristy117ff172010-08-15 21:35:32 +00002021 ssize_t
2022 j,
2023 u,
2024 v;
2025
cristy3ed852e2009-09-05 21:47:34 +00002026 assert(image != (const Image *) NULL);
2027 assert(image->signature == MagickSignature);
2028 if (image->debug != MagickFalse)
2029 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2030 assert(exception != (ExceptionInfo *) NULL);
2031 assert(exception->signature == MagickSignature);
2032 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00002033 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00002034 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002035 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00002036 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
2037 kernel_info->width=width;
2038 kernel_info->height=width;
2039 kernel_info->signature=MagickSignature;
2040 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
2041 kernel_info->width*sizeof(*kernel_info->values));
2042 if (kernel_info->values == (double *) NULL)
2043 {
2044 kernel_info=DestroyKernelInfo(kernel_info);
2045 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2046 }
2047 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00002048 i=0;
cristy47e00502009-12-17 19:19:57 +00002049 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00002050 {
cristy47e00502009-12-17 19:19:57 +00002051 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00002052 {
2053 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
2054 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2055 i++;
2056 }
cristy3ed852e2009-09-05 21:47:34 +00002057 }
cristy0a922382011-07-16 15:30:34 +00002058 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00002059 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00002060 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00002061 return(blur_image);
2062}
2063
2064/*
2065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2066% %
2067% %
2068% %
cristy3ed852e2009-09-05 21:47:34 +00002069% M o t i o n B l u r I m a g e %
2070% %
2071% %
2072% %
2073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2074%
2075% MotionBlurImage() simulates motion blur. We convolve the image with a
2076% Gaussian operator of the given radius and standard deviation (sigma).
2077% For reasonable results, radius should be larger than sigma. Use a
2078% radius of 0 and MotionBlurImage() selects a suitable radius for you.
2079% Angle gives the angle of the blurring motion.
2080%
2081% Andrew Protano contributed this effect.
2082%
2083% The format of the MotionBlurImage method is:
2084%
2085% Image *MotionBlurImage(const Image *image,const double radius,
2086% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002087%
2088% A description of each parameter follows:
2089%
2090% o image: the image.
2091%
cristy3ed852e2009-09-05 21:47:34 +00002092% o radius: the radius of the Gaussian, in pixels, not counting
2093% the center pixel.
2094%
2095% o sigma: the standard deviation of the Gaussian, in pixels.
2096%
cristycee97112010-05-28 00:44:52 +00002097% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00002098%
2099% o exception: return any errors or warnings in this structure.
2100%
2101*/
2102
cristybb503372010-05-27 20:51:26 +00002103static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00002104{
cristy3ed852e2009-09-05 21:47:34 +00002105 double
cristy47e00502009-12-17 19:19:57 +00002106 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00002107 normalize;
2108
cristybb503372010-05-27 20:51:26 +00002109 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002110 i;
2111
2112 /*
cristy47e00502009-12-17 19:19:57 +00002113 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00002114 */
2115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2116 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2117 if (kernel == (double *) NULL)
2118 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002119 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00002120 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00002121 {
cristy4205a3c2010-09-12 20:19:59 +00002122 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2123 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002124 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00002125 }
cristybb503372010-05-27 20:51:26 +00002126 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002127 kernel[i]/=normalize;
2128 return(kernel);
2129}
2130
cristyf4ad9df2011-07-08 16:49:03 +00002131MagickExport Image *MotionBlurImage(const Image *image,
2132 const double radius,const double sigma,const double angle,
2133 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002134{
cristyc4c8d132010-01-07 01:58:38 +00002135 CacheView
2136 *blur_view,
2137 *image_view;
2138
cristy3ed852e2009-09-05 21:47:34 +00002139 double
2140 *kernel;
2141
2142 Image
2143 *blur_image;
2144
cristy3ed852e2009-09-05 21:47:34 +00002145 MagickBooleanType
2146 status;
2147
cristybb503372010-05-27 20:51:26 +00002148 MagickOffsetType
2149 progress;
2150
cristy4c08aed2011-07-01 19:47:50 +00002151 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002152 bias;
cristy3ed852e2009-09-05 21:47:34 +00002153
2154 OffsetInfo
2155 *offset;
2156
2157 PointInfo
2158 point;
2159
cristybb503372010-05-27 20:51:26 +00002160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002161 i;
2162
cristybb503372010-05-27 20:51:26 +00002163 size_t
cristy3ed852e2009-09-05 21:47:34 +00002164 width;
2165
cristybb503372010-05-27 20:51:26 +00002166 ssize_t
2167 y;
2168
cristy3ed852e2009-09-05 21:47:34 +00002169 assert(image != (Image *) NULL);
2170 assert(image->signature == MagickSignature);
2171 if (image->debug != MagickFalse)
2172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2173 assert(exception != (ExceptionInfo *) NULL);
2174 width=GetOptimalKernelWidth1D(radius,sigma);
2175 kernel=GetMotionBlurKernel(width,sigma);
2176 if (kernel == (double *) NULL)
2177 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2178 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2179 if (offset == (OffsetInfo *) NULL)
2180 {
2181 kernel=(double *) RelinquishMagickMemory(kernel);
2182 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2183 }
2184 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2185 if (blur_image == (Image *) NULL)
2186 {
2187 kernel=(double *) RelinquishMagickMemory(kernel);
2188 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2189 return((Image *) NULL);
2190 }
cristy574cc262011-08-05 01:23:58 +00002191 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002192 {
2193 kernel=(double *) RelinquishMagickMemory(kernel);
2194 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
cristy3ed852e2009-09-05 21:47:34 +00002195 blur_image=DestroyImage(blur_image);
2196 return((Image *) NULL);
2197 }
2198 point.x=(double) width*sin(DegreesToRadians(angle));
2199 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00002200 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002201 {
cristybb503372010-05-27 20:51:26 +00002202 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2203 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002204 }
2205 /*
2206 Motion blur image.
2207 */
2208 status=MagickTrue;
2209 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002210 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002211 image_view=AcquireCacheView(image);
2212 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00002213#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00002214 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00002215#endif
cristybb503372010-05-27 20:51:26 +00002216 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002217 {
cristy4c08aed2011-07-01 19:47:50 +00002218 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002219 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002220
cristy117ff172010-08-15 21:35:32 +00002221 register ssize_t
2222 x;
2223
cristy3ed852e2009-09-05 21:47:34 +00002224 if (status == MagickFalse)
2225 continue;
2226 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2227 exception);
cristyacd2ed22011-08-30 01:44:23 +00002228 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002229 {
2230 status=MagickFalse;
2231 continue;
2232 }
cristybb503372010-05-27 20:51:26 +00002233 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002234 {
cristy4c08aed2011-07-01 19:47:50 +00002235 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002236 qixel;
2237
2238 PixelPacket
2239 pixel;
2240
2241 register double
cristyc47d1f82009-11-26 01:44:43 +00002242 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00002243
cristybb503372010-05-27 20:51:26 +00002244 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002245 i;
2246
cristy3ed852e2009-09-05 21:47:34 +00002247 k=kernel;
cristyddd82202009-11-03 20:14:50 +00002248 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002249 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002250 {
cristybb503372010-05-27 20:51:26 +00002251 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002252 {
2253 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2254 offset[i].y,&pixel,exception);
2255 qixel.red+=(*k)*pixel.red;
2256 qixel.green+=(*k)*pixel.green;
2257 qixel.blue+=(*k)*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002258 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002259 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002260 qixel.black+=(*k)*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002261 k++;
2262 }
cristyed231572011-07-14 02:18:59 +00002263 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002264 SetPixelRed(blur_image,
2265 ClampToQuantum(qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002266 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002267 SetPixelGreen(blur_image,
2268 ClampToQuantum(qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002269 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002270 SetPixelBlue(blur_image,
2271 ClampToQuantum(qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002272 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002273 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002274 SetPixelBlack(blur_image,
2275 ClampToQuantum(qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002276 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002277 SetPixelAlpha(blur_image,
2278 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002279 }
2280 else
2281 {
2282 MagickRealType
2283 alpha,
2284 gamma;
2285
2286 alpha=0.0;
2287 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00002288 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002289 {
2290 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2291 offset[i].y,&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00002292 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00002293 qixel.red+=(*k)*alpha*pixel.red;
2294 qixel.green+=(*k)*alpha*pixel.green;
2295 qixel.blue+=(*k)*alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002296 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002297 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002298 qixel.black+=(*k)*alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002299 gamma+=(*k)*alpha;
2300 k++;
2301 }
2302 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00002303 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002304 SetPixelRed(blur_image,
2305 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002306 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002307 SetPixelGreen(blur_image,
2308 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002309 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002310 SetPixelBlue(blur_image,
2311 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002312 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002313 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002314 SetPixelBlack(blur_image,
2315 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002316 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002317 SetPixelAlpha(blur_image,
2318 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002319 }
cristyed231572011-07-14 02:18:59 +00002320 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002321 }
2322 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2323 status=MagickFalse;
2324 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2325 {
2326 MagickBooleanType
2327 proceed;
2328
cristyb557a152011-02-22 12:14:30 +00002329#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002330 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002331#endif
2332 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2333 if (proceed == MagickFalse)
2334 status=MagickFalse;
2335 }
2336 }
2337 blur_view=DestroyCacheView(blur_view);
2338 image_view=DestroyCacheView(image_view);
2339 kernel=(double *) RelinquishMagickMemory(kernel);
2340 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2341 if (status == MagickFalse)
2342 blur_image=DestroyImage(blur_image);
2343 return(blur_image);
2344}
2345
2346/*
2347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2348% %
2349% %
2350% %
2351% P r e v i e w I m a g e %
2352% %
2353% %
2354% %
2355%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2356%
2357% PreviewImage() tiles 9 thumbnails of the specified image with an image
2358% processing operation applied with varying parameters. This may be helpful
2359% pin-pointing an appropriate parameter for a particular image processing
2360% operation.
2361%
2362% The format of the PreviewImages method is:
2363%
2364% Image *PreviewImages(const Image *image,const PreviewType preview,
2365% ExceptionInfo *exception)
2366%
2367% A description of each parameter follows:
2368%
2369% o image: the image.
2370%
2371% o preview: the image processing operation.
2372%
2373% o exception: return any errors or warnings in this structure.
2374%
2375*/
2376MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2377 ExceptionInfo *exception)
2378{
2379#define NumberTiles 9
2380#define PreviewImageTag "Preview/Image"
2381#define DefaultPreviewGeometry "204x204+10+10"
2382
2383 char
2384 factor[MaxTextExtent],
2385 label[MaxTextExtent];
2386
2387 double
2388 degrees,
2389 gamma,
2390 percentage,
2391 radius,
2392 sigma,
2393 threshold;
2394
2395 Image
2396 *images,
2397 *montage_image,
2398 *preview_image,
2399 *thumbnail;
2400
2401 ImageInfo
2402 *preview_info;
2403
cristy3ed852e2009-09-05 21:47:34 +00002404 MagickBooleanType
2405 proceed;
2406
2407 MontageInfo
2408 *montage_info;
2409
2410 QuantizeInfo
2411 quantize_info;
2412
2413 RectangleInfo
2414 geometry;
2415
cristybb503372010-05-27 20:51:26 +00002416 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002417 i,
2418 x;
2419
cristybb503372010-05-27 20:51:26 +00002420 size_t
cristy3ed852e2009-09-05 21:47:34 +00002421 colors;
2422
cristy117ff172010-08-15 21:35:32 +00002423 ssize_t
2424 y;
2425
cristy3ed852e2009-09-05 21:47:34 +00002426 /*
2427 Open output image file.
2428 */
2429 assert(image != (Image *) NULL);
2430 assert(image->signature == MagickSignature);
2431 if (image->debug != MagickFalse)
2432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2433 colors=2;
2434 degrees=0.0;
2435 gamma=(-0.2f);
2436 preview_info=AcquireImageInfo();
2437 SetGeometry(image,&geometry);
2438 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2439 &geometry.width,&geometry.height);
2440 images=NewImageList();
2441 percentage=12.5;
2442 GetQuantizeInfo(&quantize_info);
2443 radius=0.0;
2444 sigma=1.0;
2445 threshold=0.0;
2446 x=0;
2447 y=0;
2448 for (i=0; i < NumberTiles; i++)
2449 {
2450 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2451 if (thumbnail == (Image *) NULL)
2452 break;
2453 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2454 (void *) NULL);
2455 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2456 if (i == (NumberTiles/2))
2457 {
2458 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2459 AppendImageToList(&images,thumbnail);
2460 continue;
2461 }
2462 switch (preview)
2463 {
2464 case RotatePreview:
2465 {
2466 degrees+=45.0;
2467 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002468 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002469 break;
2470 }
2471 case ShearPreview:
2472 {
2473 degrees+=5.0;
2474 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002475 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002476 degrees,2.0*degrees);
2477 break;
2478 }
2479 case RollPreview:
2480 {
cristybb503372010-05-27 20:51:26 +00002481 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2482 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002483 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002484 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002485 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002486 break;
2487 }
2488 case HuePreview:
2489 {
2490 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2491 if (preview_image == (Image *) NULL)
2492 break;
cristyb51dff52011-05-19 16:55:47 +00002493 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002494 2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002495 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002496 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002497 break;
2498 }
2499 case SaturationPreview:
2500 {
2501 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2502 if (preview_image == (Image *) NULL)
2503 break;
cristyb51dff52011-05-19 16:55:47 +00002504 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002505 2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002506 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002507 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002508 break;
2509 }
2510 case BrightnessPreview:
2511 {
2512 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2513 if (preview_image == (Image *) NULL)
2514 break;
cristyb51dff52011-05-19 16:55:47 +00002515 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002516 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002517 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002518 break;
2519 }
2520 case GammaPreview:
2521 default:
2522 {
2523 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2524 if (preview_image == (Image *) NULL)
2525 break;
2526 gamma+=0.4f;
cristyb3e7c6c2011-07-24 01:43:55 +00002527 (void) GammaImage(preview_image,gamma,exception);
cristyb51dff52011-05-19 16:55:47 +00002528 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002529 break;
2530 }
2531 case SpiffPreview:
2532 {
2533 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2534 if (preview_image != (Image *) NULL)
2535 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002536 (void) ContrastImage(preview_image,MagickTrue,exception);
cristyb51dff52011-05-19 16:55:47 +00002537 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002538 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002539 break;
2540 }
2541 case DullPreview:
2542 {
2543 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2544 if (preview_image == (Image *) NULL)
2545 break;
2546 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002547 (void) ContrastImage(preview_image,MagickFalse,exception);
cristyb51dff52011-05-19 16:55:47 +00002548 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002549 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002550 break;
2551 }
2552 case GrayscalePreview:
2553 {
2554 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2555 if (preview_image == (Image *) NULL)
2556 break;
2557 colors<<=1;
2558 quantize_info.number_colors=colors;
2559 quantize_info.colorspace=GRAYColorspace;
cristy018f07f2011-09-04 21:15:19 +00002560 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristyb51dff52011-05-19 16:55:47 +00002561 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002562 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002563 break;
2564 }
2565 case QuantizePreview:
2566 {
2567 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2568 if (preview_image == (Image *) NULL)
2569 break;
2570 colors<<=1;
2571 quantize_info.number_colors=colors;
cristy018f07f2011-09-04 21:15:19 +00002572 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristyb51dff52011-05-19 16:55:47 +00002573 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002574 colors);
cristy3ed852e2009-09-05 21:47:34 +00002575 break;
2576 }
2577 case DespecklePreview:
2578 {
2579 for (x=0; x < (i-1); x++)
2580 {
2581 preview_image=DespeckleImage(thumbnail,exception);
2582 if (preview_image == (Image *) NULL)
2583 break;
2584 thumbnail=DestroyImage(thumbnail);
2585 thumbnail=preview_image;
2586 }
2587 preview_image=DespeckleImage(thumbnail,exception);
2588 if (preview_image == (Image *) NULL)
2589 break;
cristyb51dff52011-05-19 16:55:47 +00002590 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002591 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002592 break;
2593 }
2594 case ReduceNoisePreview:
2595 {
cristy95c38342011-03-18 22:39:51 +00002596 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2597 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002598 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002599 break;
2600 }
2601 case AddNoisePreview:
2602 {
2603 switch ((int) i)
2604 {
2605 case 0:
2606 {
2607 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2608 break;
2609 }
2610 case 1:
2611 {
2612 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2613 break;
2614 }
2615 case 2:
2616 {
2617 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2618 break;
2619 }
2620 case 3:
2621 {
2622 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2623 break;
2624 }
2625 case 4:
2626 {
2627 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2628 break;
2629 }
2630 case 5:
2631 {
2632 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2633 break;
2634 }
2635 default:
2636 {
2637 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2638 break;
2639 }
2640 }
cristyd76c51e2011-03-26 00:21:26 +00002641 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2642 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002643 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002644 break;
2645 }
2646 case SharpenPreview:
2647 {
2648 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002649 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002650 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002651 break;
2652 }
2653 case BlurPreview:
2654 {
2655 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002656 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002657 sigma);
2658 break;
2659 }
2660 case ThresholdPreview:
2661 {
2662 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2663 if (preview_image == (Image *) NULL)
2664 break;
2665 (void) BilevelImage(thumbnail,
2666 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
cristyb51dff52011-05-19 16:55:47 +00002667 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002668 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2669 break;
2670 }
2671 case EdgeDetectPreview:
2672 {
cristy8ae632d2011-09-05 17:29:53 +00002673 preview_image=EdgeImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002674 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002675 break;
2676 }
2677 case SpreadPreview:
2678 {
2679 preview_image=SpreadImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002680 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002681 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002682 break;
2683 }
2684 case SolarizePreview:
2685 {
2686 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2687 if (preview_image == (Image *) NULL)
2688 break;
2689 (void) SolarizeImage(preview_image,(double) QuantumRange*
cristy5cbc0162011-08-29 00:36:28 +00002690 percentage/100.0,exception);
cristyb51dff52011-05-19 16:55:47 +00002691 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002692 (QuantumRange*percentage)/100.0);
2693 break;
2694 }
2695 case ShadePreview:
2696 {
2697 degrees+=10.0;
2698 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2699 exception);
cristyb51dff52011-05-19 16:55:47 +00002700 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002701 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002702 break;
2703 }
2704 case RaisePreview:
2705 {
2706 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2707 if (preview_image == (Image *) NULL)
2708 break;
cristybb503372010-05-27 20:51:26 +00002709 geometry.width=(size_t) (2*i+2);
2710 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002711 geometry.x=i/2;
2712 geometry.y=i/2;
cristy6170ac32011-08-28 14:15:37 +00002713 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
cristyb51dff52011-05-19 16:55:47 +00002714 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002715 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002716 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002717 break;
2718 }
2719 case SegmentPreview:
2720 {
2721 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2722 if (preview_image == (Image *) NULL)
2723 break;
2724 threshold+=0.4f;
2725 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
cristy018f07f2011-09-04 21:15:19 +00002726 threshold,exception);
cristyb51dff52011-05-19 16:55:47 +00002727 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002728 threshold,threshold);
2729 break;
2730 }
2731 case SwirlPreview:
2732 {
2733 preview_image=SwirlImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002734 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002735 degrees+=45.0;
2736 break;
2737 }
2738 case ImplodePreview:
2739 {
2740 degrees+=0.1f;
2741 preview_image=ImplodeImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002742 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002743 break;
2744 }
2745 case WavePreview:
2746 {
2747 degrees+=5.0f;
2748 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002749 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002750 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002751 break;
2752 }
2753 case OilPaintPreview:
2754 {
cristy14973ba2011-08-27 23:48:07 +00002755 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2756 exception);
2757 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2758 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002759 break;
2760 }
2761 case CharcoalDrawingPreview:
2762 {
2763 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2764 exception);
cristyb51dff52011-05-19 16:55:47 +00002765 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002766 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002767 break;
2768 }
2769 case JPEGPreview:
2770 {
2771 char
2772 filename[MaxTextExtent];
2773
2774 int
2775 file;
2776
2777 MagickBooleanType
2778 status;
2779
2780 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2781 if (preview_image == (Image *) NULL)
2782 break;
cristybb503372010-05-27 20:51:26 +00002783 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002784 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002785 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002786 file=AcquireUniqueFileResource(filename);
2787 if (file != -1)
2788 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002789 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002790 "jpeg:%s",filename);
cristy6f9e0d32011-08-28 16:32:09 +00002791 status=WriteImage(preview_info,preview_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002792 if (status != MagickFalse)
2793 {
2794 Image
2795 *quality_image;
2796
2797 (void) CopyMagickString(preview_info->filename,
2798 preview_image->filename,MaxTextExtent);
2799 quality_image=ReadImage(preview_info,exception);
2800 if (quality_image != (Image *) NULL)
2801 {
2802 preview_image=DestroyImage(preview_image);
2803 preview_image=quality_image;
2804 }
2805 }
2806 (void) RelinquishUniqueFileResource(preview_image->filename);
2807 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002808 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002809 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2810 1024.0/1024.0);
2811 else
2812 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002813 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002814 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002815 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002816 else
cristyb51dff52011-05-19 16:55:47 +00002817 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002818 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002819 break;
2820 }
2821 }
2822 thumbnail=DestroyImage(thumbnail);
2823 percentage+=12.5;
2824 radius+=0.5;
2825 sigma+=0.25;
2826 if (preview_image == (Image *) NULL)
2827 break;
2828 (void) DeleteImageProperty(preview_image,"label");
2829 (void) SetImageProperty(preview_image,"label",label);
2830 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002831 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2832 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002833 if (proceed == MagickFalse)
2834 break;
2835 }
2836 if (images == (Image *) NULL)
2837 {
2838 preview_info=DestroyImageInfo(preview_info);
2839 return((Image *) NULL);
2840 }
2841 /*
2842 Create the montage.
2843 */
2844 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2845 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2846 montage_info->shadow=MagickTrue;
2847 (void) CloneString(&montage_info->tile,"3x3");
2848 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2849 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2850 montage_image=MontageImages(images,montage_info,exception);
2851 montage_info=DestroyMontageInfo(montage_info);
2852 images=DestroyImageList(images);
2853 if (montage_image == (Image *) NULL)
2854 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2855 if (montage_image->montage != (char *) NULL)
2856 {
2857 /*
2858 Free image directory.
2859 */
2860 montage_image->montage=(char *) RelinquishMagickMemory(
2861 montage_image->montage);
2862 if (image->directory != (char *) NULL)
2863 montage_image->directory=(char *) RelinquishMagickMemory(
2864 montage_image->directory);
2865 }
2866 preview_info=DestroyImageInfo(preview_info);
2867 return(montage_image);
2868}
2869
2870/*
2871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2872% %
2873% %
2874% %
2875% R a d i a l B l u r I m a g e %
2876% %
2877% %
2878% %
2879%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2880%
2881% RadialBlurImage() applies a radial blur to the image.
2882%
2883% Andrew Protano contributed this effect.
2884%
2885% The format of the RadialBlurImage method is:
2886%
2887% Image *RadialBlurImage(const Image *image,const double angle,
2888% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002889%
2890% A description of each parameter follows:
2891%
2892% o image: the image.
2893%
cristy3ed852e2009-09-05 21:47:34 +00002894% o angle: the angle of the radial blur.
2895%
2896% o exception: return any errors or warnings in this structure.
2897%
2898*/
cristyf4ad9df2011-07-08 16:49:03 +00002899MagickExport Image *RadialBlurImage(const Image *image,
2900 const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002901{
cristyc4c8d132010-01-07 01:58:38 +00002902 CacheView
2903 *blur_view,
2904 *image_view;
2905
cristy3ed852e2009-09-05 21:47:34 +00002906 Image
2907 *blur_image;
2908
cristy3ed852e2009-09-05 21:47:34 +00002909 MagickBooleanType
2910 status;
2911
cristybb503372010-05-27 20:51:26 +00002912 MagickOffsetType
2913 progress;
2914
cristy4c08aed2011-07-01 19:47:50 +00002915 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002916 bias;
cristy3ed852e2009-09-05 21:47:34 +00002917
2918 MagickRealType
2919 blur_radius,
2920 *cos_theta,
2921 offset,
2922 *sin_theta,
2923 theta;
2924
2925 PointInfo
2926 blur_center;
2927
cristybb503372010-05-27 20:51:26 +00002928 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002929 i;
2930
cristybb503372010-05-27 20:51:26 +00002931 size_t
cristy3ed852e2009-09-05 21:47:34 +00002932 n;
2933
cristybb503372010-05-27 20:51:26 +00002934 ssize_t
2935 y;
2936
cristy3ed852e2009-09-05 21:47:34 +00002937 /*
2938 Allocate blur image.
2939 */
2940 assert(image != (Image *) NULL);
2941 assert(image->signature == MagickSignature);
2942 if (image->debug != MagickFalse)
2943 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2944 assert(exception != (ExceptionInfo *) NULL);
2945 assert(exception->signature == MagickSignature);
2946 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2947 if (blur_image == (Image *) NULL)
2948 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002949 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002950 {
cristy3ed852e2009-09-05 21:47:34 +00002951 blur_image=DestroyImage(blur_image);
2952 return((Image *) NULL);
2953 }
2954 blur_center.x=(double) image->columns/2.0;
2955 blur_center.y=(double) image->rows/2.0;
2956 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002957 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002958 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2959 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2960 sizeof(*cos_theta));
2961 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2962 sizeof(*sin_theta));
2963 if ((cos_theta == (MagickRealType *) NULL) ||
2964 (sin_theta == (MagickRealType *) NULL))
2965 {
2966 blur_image=DestroyImage(blur_image);
2967 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2968 }
2969 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002970 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002971 {
2972 cos_theta[i]=cos((double) (theta*i-offset));
2973 sin_theta[i]=sin((double) (theta*i-offset));
2974 }
2975 /*
2976 Radial blur image.
2977 */
2978 status=MagickTrue;
2979 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002980 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002981 image_view=AcquireCacheView(image);
2982 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002983#if defined(MAGICKCORE_OPENMP_SUPPORT)
2984 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002985#endif
cristybb503372010-05-27 20:51:26 +00002986 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002987 {
cristy4c08aed2011-07-01 19:47:50 +00002988 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002989 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002990
cristy117ff172010-08-15 21:35:32 +00002991 register ssize_t
2992 x;
2993
cristy3ed852e2009-09-05 21:47:34 +00002994 if (status == MagickFalse)
2995 continue;
2996 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2997 exception);
cristyacd2ed22011-08-30 01:44:23 +00002998 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 status=MagickFalse;
3001 continue;
3002 }
cristybb503372010-05-27 20:51:26 +00003003 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003004 {
cristy4c08aed2011-07-01 19:47:50 +00003005 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003006 qixel;
3007
3008 MagickRealType
3009 normalize,
3010 radius;
3011
3012 PixelPacket
3013 pixel;
3014
3015 PointInfo
3016 center;
3017
cristybb503372010-05-27 20:51:26 +00003018 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003019 i;
3020
cristybb503372010-05-27 20:51:26 +00003021 size_t
cristy3ed852e2009-09-05 21:47:34 +00003022 step;
3023
3024 center.x=(double) x-blur_center.x;
3025 center.y=(double) y-blur_center.y;
3026 radius=hypot((double) center.x,center.y);
3027 if (radius == 0)
3028 step=1;
3029 else
3030 {
cristybb503372010-05-27 20:51:26 +00003031 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00003032 if (step == 0)
3033 step=1;
3034 else
3035 if (step >= n)
3036 step=n-1;
3037 }
3038 normalize=0.0;
cristyddd82202009-11-03 20:14:50 +00003039 qixel=bias;
cristyed231572011-07-14 02:18:59 +00003040 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003041 {
cristyeaedf062010-05-29 22:36:02 +00003042 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003043 {
cristyeaedf062010-05-29 22:36:02 +00003044 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3045 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3046 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3047 cos_theta[i]+0.5),&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00003048 qixel.red+=pixel.red;
3049 qixel.green+=pixel.green;
3050 qixel.blue+=pixel.blue;
cristy3ed852e2009-09-05 21:47:34 +00003051 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003052 qixel.black+=pixel.black;
3053 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003054 normalize+=1.0;
3055 }
3056 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3057 normalize);
cristyed231572011-07-14 02:18:59 +00003058 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003059 SetPixelRed(blur_image,
3060 ClampToQuantum(normalize*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003061 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003062 SetPixelGreen(blur_image,
3063 ClampToQuantum(normalize*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003064 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003065 SetPixelBlue(blur_image,
3066 ClampToQuantum(normalize*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003067 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003068 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003069 SetPixelBlack(blur_image,
3070 ClampToQuantum(normalize*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003071 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003072 SetPixelAlpha(blur_image,
3073 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003074 }
3075 else
3076 {
3077 MagickRealType
3078 alpha,
3079 gamma;
3080
3081 alpha=1.0;
3082 gamma=0.0;
cristyeaedf062010-05-29 22:36:02 +00003083 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003084 {
cristyeaedf062010-05-29 22:36:02 +00003085 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3086 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3087 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3088 cos_theta[i]+0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003089 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00003090 qixel.red+=alpha*pixel.red;
3091 qixel.green+=alpha*pixel.green;
3092 qixel.blue+=alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00003093 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003094 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003095 qixel.black+=alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00003096 gamma+=alpha;
3097 normalize+=1.0;
3098 }
3099 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3100 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3101 normalize);
cristyed231572011-07-14 02:18:59 +00003102 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003103 SetPixelRed(blur_image,
3104 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003105 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003106 SetPixelGreen(blur_image,
3107 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003108 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003109 SetPixelBlue(blur_image,
3110 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003111 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003112 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003113 SetPixelBlack(blur_image,
3114 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003115 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003116 SetPixelAlpha(blur_image,
3117 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003118 }
cristyed231572011-07-14 02:18:59 +00003119 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003120 }
3121 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3122 status=MagickFalse;
3123 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3124 {
3125 MagickBooleanType
3126 proceed;
3127
cristyb5d5f722009-11-04 03:03:49 +00003128#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003129 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003130#endif
3131 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3132 if (proceed == MagickFalse)
3133 status=MagickFalse;
3134 }
3135 }
3136 blur_view=DestroyCacheView(blur_view);
3137 image_view=DestroyCacheView(image_view);
3138 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3139 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3140 if (status == MagickFalse)
3141 blur_image=DestroyImage(blur_image);
3142 return(blur_image);
3143}
3144
3145/*
3146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3147% %
3148% %
3149% %
cristy3ed852e2009-09-05 21:47:34 +00003150% S e l e c t i v e B l u r I m a g e %
3151% %
3152% %
3153% %
3154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3155%
3156% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3157% It is similar to the unsharpen mask that sharpens everything with contrast
3158% above a certain threshold.
3159%
3160% The format of the SelectiveBlurImage method is:
3161%
3162% Image *SelectiveBlurImage(const Image *image,const double radius,
3163% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003164%
3165% A description of each parameter follows:
3166%
3167% o image: the image.
3168%
cristy3ed852e2009-09-05 21:47:34 +00003169% o radius: the radius of the Gaussian, in pixels, not counting the center
3170% pixel.
3171%
3172% o sigma: the standard deviation of the Gaussian, in pixels.
3173%
3174% o threshold: only pixels within this contrast threshold are included
3175% in the blur operation.
3176%
3177% o exception: return any errors or warnings in this structure.
3178%
3179*/
cristyf4ad9df2011-07-08 16:49:03 +00003180MagickExport Image *SelectiveBlurImage(const Image *image,
3181 const double radius,const double sigma,const double threshold,
3182 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003183{
3184#define SelectiveBlurImageTag "SelectiveBlur/Image"
3185
cristy47e00502009-12-17 19:19:57 +00003186 CacheView
3187 *blur_view,
3188 *image_view;
3189
cristy3ed852e2009-09-05 21:47:34 +00003190 double
cristy3ed852e2009-09-05 21:47:34 +00003191 *kernel;
3192
3193 Image
3194 *blur_image;
3195
cristy3ed852e2009-09-05 21:47:34 +00003196 MagickBooleanType
3197 status;
3198
cristybb503372010-05-27 20:51:26 +00003199 MagickOffsetType
3200 progress;
3201
cristy4c08aed2011-07-01 19:47:50 +00003202 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003203 bias;
3204
cristybb503372010-05-27 20:51:26 +00003205 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003206 i;
cristy3ed852e2009-09-05 21:47:34 +00003207
cristybb503372010-05-27 20:51:26 +00003208 size_t
cristy3ed852e2009-09-05 21:47:34 +00003209 width;
3210
cristybb503372010-05-27 20:51:26 +00003211 ssize_t
3212 j,
3213 u,
3214 v,
3215 y;
3216
cristy3ed852e2009-09-05 21:47:34 +00003217 /*
3218 Initialize blur image attributes.
3219 */
3220 assert(image != (Image *) NULL);
3221 assert(image->signature == MagickSignature);
3222 if (image->debug != MagickFalse)
3223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3224 assert(exception != (ExceptionInfo *) NULL);
3225 assert(exception->signature == MagickSignature);
3226 width=GetOptimalKernelWidth1D(radius,sigma);
3227 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3228 if (kernel == (double *) NULL)
3229 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003230 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00003231 i=0;
cristy47e00502009-12-17 19:19:57 +00003232 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003233 {
cristy47e00502009-12-17 19:19:57 +00003234 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00003235 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3236 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00003237 }
3238 if (image->debug != MagickFalse)
3239 {
3240 char
3241 format[MaxTextExtent],
3242 *message;
3243
cristy117ff172010-08-15 21:35:32 +00003244 register const double
3245 *k;
3246
cristybb503372010-05-27 20:51:26 +00003247 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003248 u,
3249 v;
3250
cristy3ed852e2009-09-05 21:47:34 +00003251 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003252 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3253 width);
cristy3ed852e2009-09-05 21:47:34 +00003254 message=AcquireString("");
3255 k=kernel;
cristybb503372010-05-27 20:51:26 +00003256 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003257 {
3258 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00003259 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00003260 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00003261 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003262 {
cristyb51dff52011-05-19 16:55:47 +00003263 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00003264 (void) ConcatenateString(&message,format);
3265 }
3266 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3267 }
3268 message=DestroyString(message);
3269 }
3270 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3271 if (blur_image == (Image *) NULL)
3272 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003273 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003274 {
cristy3ed852e2009-09-05 21:47:34 +00003275 blur_image=DestroyImage(blur_image);
3276 return((Image *) NULL);
3277 }
3278 /*
3279 Threshold blur image.
3280 */
3281 status=MagickTrue;
3282 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003283 GetPixelInfo(image,&bias);
3284 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003285 image_view=AcquireCacheView(image);
3286 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003287#if defined(MAGICKCORE_OPENMP_SUPPORT)
3288 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003289#endif
cristybb503372010-05-27 20:51:26 +00003290 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003291 {
cristy4c08aed2011-07-01 19:47:50 +00003292 double
3293 contrast;
3294
cristy3ed852e2009-09-05 21:47:34 +00003295 MagickBooleanType
3296 sync;
3297
3298 MagickRealType
3299 gamma;
3300
cristy4c08aed2011-07-01 19:47:50 +00003301 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003302 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003303
cristy4c08aed2011-07-01 19:47:50 +00003304 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003305 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003306
cristy117ff172010-08-15 21:35:32 +00003307 register ssize_t
3308 x;
3309
cristy3ed852e2009-09-05 21:47:34 +00003310 if (status == MagickFalse)
3311 continue;
cristy117ff172010-08-15 21:35:32 +00003312 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3313 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +00003314 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3315 exception);
cristy4c08aed2011-07-01 19:47:50 +00003316 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003317 {
3318 status=MagickFalse;
3319 continue;
3320 }
cristybb503372010-05-27 20:51:26 +00003321 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003322 {
cristy4c08aed2011-07-01 19:47:50 +00003323 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003324 pixel;
3325
3326 register const double
cristyc47d1f82009-11-26 01:44:43 +00003327 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00003328
cristybb503372010-05-27 20:51:26 +00003329 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003330 u;
3331
cristy117ff172010-08-15 21:35:32 +00003332 ssize_t
3333 j,
3334 v;
3335
cristyddd82202009-11-03 20:14:50 +00003336 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00003337 k=kernel;
3338 gamma=0.0;
3339 j=0;
cristyed231572011-07-14 02:18:59 +00003340 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003341 {
cristybb503372010-05-27 20:51:26 +00003342 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003343 {
cristybb503372010-05-27 20:51:26 +00003344 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003345 {
cristyed231572011-07-14 02:18:59 +00003346 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003347 (double) GetPixelIntensity(blur_image,q);
3348 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003349 {
cristy4c08aed2011-07-01 19:47:50 +00003350 pixel.red+=(*k)*
cristyed231572011-07-14 02:18:59 +00003351 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003352 pixel.green+=(*k)*
cristyed231572011-07-14 02:18:59 +00003353 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003354 pixel.blue+=(*k)*
cristyed231572011-07-14 02:18:59 +00003355 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003356 if (image->colorspace == CMYKColorspace)
3357 pixel.black+=(*k)*
cristyed231572011-07-14 02:18:59 +00003358 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003359 gamma+=(*k);
3360 k++;
3361 }
3362 }
cristyd99b0962010-05-29 23:14:26 +00003363 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003364 }
3365 if (gamma != 0.0)
3366 {
3367 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003368 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003369 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003370 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003371 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003372 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003373 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003374 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003375 (image->colorspace == CMYKColorspace))
3376 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003377 }
cristyed231572011-07-14 02:18:59 +00003378 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003379 {
3380 gamma=0.0;
3381 j=0;
cristybb503372010-05-27 20:51:26 +00003382 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003383 {
cristybb503372010-05-27 20:51:26 +00003384 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003385 {
cristy4c08aed2011-07-01 19:47:50 +00003386 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003387 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003388 GetPixelIntensity(blur_image,q);
3389 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003390 {
cristy4c08aed2011-07-01 19:47:50 +00003391 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003392 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003393 gamma+=(*k);
3394 k++;
3395 }
3396 }
cristyeaedf062010-05-29 22:36:02 +00003397 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003398 }
3399 if (gamma != 0.0)
3400 {
3401 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3402 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003403 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003404 }
3405 }
3406 }
3407 else
3408 {
3409 MagickRealType
3410 alpha;
3411
cristybb503372010-05-27 20:51:26 +00003412 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003413 {
cristybb503372010-05-27 20:51:26 +00003414 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003415 {
cristy4c08aed2011-07-01 19:47:50 +00003416 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003417 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003418 GetPixelIntensity(blur_image,q);
3419 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003420 {
cristy4c08aed2011-07-01 19:47:50 +00003421 alpha=(MagickRealType) (QuantumScale*
cristyed231572011-07-14 02:18:59 +00003422 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
cristy4c08aed2011-07-01 19:47:50 +00003423 pixel.red+=(*k)*alpha*
cristyed231572011-07-14 02:18:59 +00003424 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003425 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003426 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003427 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003428 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003429 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003430 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003431 if (image->colorspace == CMYKColorspace)
3432 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003433 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003434 gamma+=(*k)*alpha;
3435 k++;
3436 }
3437 }
cristyeaedf062010-05-29 22:36:02 +00003438 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003439 }
3440 if (gamma != 0.0)
3441 {
3442 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003443 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003444 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003445 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003446 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003447 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003448 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003449 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003450 (image->colorspace == CMYKColorspace))
3451 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003452 }
cristyed231572011-07-14 02:18:59 +00003453 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003454 {
3455 gamma=0.0;
3456 j=0;
cristybb503372010-05-27 20:51:26 +00003457 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003458 {
cristybb503372010-05-27 20:51:26 +00003459 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003460 {
cristy4c08aed2011-07-01 19:47:50 +00003461 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003462 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003463 GetPixelIntensity(blur_image,q);
3464 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003465 {
cristy4c08aed2011-07-01 19:47:50 +00003466 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003467 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003468 gamma+=(*k);
3469 k++;
3470 }
3471 }
cristyeaedf062010-05-29 22:36:02 +00003472 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003473 }
3474 if (gamma != 0.0)
3475 {
3476 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3477 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003478 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003479 }
3480 }
3481 }
cristyed231572011-07-14 02:18:59 +00003482 p+=GetPixelChannels(image);
3483 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003484 }
3485 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3486 if (sync == MagickFalse)
3487 status=MagickFalse;
3488 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3489 {
3490 MagickBooleanType
3491 proceed;
3492
cristyb5d5f722009-11-04 03:03:49 +00003493#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003494 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003495#endif
3496 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3497 image->rows);
3498 if (proceed == MagickFalse)
3499 status=MagickFalse;
3500 }
3501 }
3502 blur_image->type=image->type;
3503 blur_view=DestroyCacheView(blur_view);
3504 image_view=DestroyCacheView(image_view);
3505 kernel=(double *) RelinquishMagickMemory(kernel);
3506 if (status == MagickFalse)
3507 blur_image=DestroyImage(blur_image);
3508 return(blur_image);
3509}
3510
3511/*
3512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3513% %
3514% %
3515% %
3516% S h a d e I m a g e %
3517% %
3518% %
3519% %
3520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3521%
3522% ShadeImage() shines a distant light on an image to create a
3523% three-dimensional effect. You control the positioning of the light with
3524% azimuth and elevation; azimuth is measured in degrees off the x axis
3525% and elevation is measured in pixels above the Z axis.
3526%
3527% The format of the ShadeImage method is:
3528%
3529% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3530% const double azimuth,const double elevation,ExceptionInfo *exception)
3531%
3532% A description of each parameter follows:
3533%
3534% o image: the image.
3535%
3536% o gray: A value other than zero shades the intensity of each pixel.
3537%
3538% o azimuth, elevation: Define the light source direction.
3539%
3540% o exception: return any errors or warnings in this structure.
3541%
3542*/
3543MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3544 const double azimuth,const double elevation,ExceptionInfo *exception)
3545{
3546#define ShadeImageTag "Shade/Image"
3547
cristyc4c8d132010-01-07 01:58:38 +00003548 CacheView
3549 *image_view,
3550 *shade_view;
3551
cristy3ed852e2009-09-05 21:47:34 +00003552 Image
3553 *shade_image;
3554
cristy3ed852e2009-09-05 21:47:34 +00003555 MagickBooleanType
3556 status;
3557
cristybb503372010-05-27 20:51:26 +00003558 MagickOffsetType
3559 progress;
3560
cristy3ed852e2009-09-05 21:47:34 +00003561 PrimaryInfo
3562 light;
3563
cristybb503372010-05-27 20:51:26 +00003564 ssize_t
3565 y;
3566
cristy3ed852e2009-09-05 21:47:34 +00003567 /*
3568 Initialize shaded image attributes.
3569 */
3570 assert(image != (const Image *) NULL);
3571 assert(image->signature == MagickSignature);
3572 if (image->debug != MagickFalse)
3573 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3574 assert(exception != (ExceptionInfo *) NULL);
3575 assert(exception->signature == MagickSignature);
3576 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3577 if (shade_image == (Image *) NULL)
3578 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003579 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003580 {
cristy3ed852e2009-09-05 21:47:34 +00003581 shade_image=DestroyImage(shade_image);
3582 return((Image *) NULL);
3583 }
3584 /*
3585 Compute the light vector.
3586 */
3587 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3588 cos(DegreesToRadians(elevation));
3589 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3590 cos(DegreesToRadians(elevation));
3591 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3592 /*
3593 Shade image.
3594 */
3595 status=MagickTrue;
3596 progress=0;
3597 image_view=AcquireCacheView(image);
3598 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003599#if defined(MAGICKCORE_OPENMP_SUPPORT)
3600 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003601#endif
cristybb503372010-05-27 20:51:26 +00003602 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003603 {
3604 MagickRealType
3605 distance,
3606 normal_distance,
3607 shade;
3608
3609 PrimaryInfo
3610 normal;
3611
cristy4c08aed2011-07-01 19:47:50 +00003612 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003613 *restrict p,
3614 *restrict s0,
3615 *restrict s1,
3616 *restrict s2;
cristy3ed852e2009-09-05 21:47:34 +00003617
cristy4c08aed2011-07-01 19:47:50 +00003618 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003619 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003620
cristy117ff172010-08-15 21:35:32 +00003621 register ssize_t
3622 x;
3623
cristy3ed852e2009-09-05 21:47:34 +00003624 if (status == MagickFalse)
3625 continue;
3626 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3627 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3628 exception);
cristy4c08aed2011-07-01 19:47:50 +00003629 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003630 {
3631 status=MagickFalse;
3632 continue;
3633 }
3634 /*
3635 Shade this row of pixels.
3636 */
3637 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristyed231572011-07-14 02:18:59 +00003638 s0=p+GetPixelChannels(image);
3639 s1=s0+(image->columns+2)*GetPixelChannels(image);
3640 s2=s1+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003641 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003642 {
3643 /*
3644 Determine the surface normal and compute shading.
3645 */
cristyed231572011-07-14 02:18:59 +00003646 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3647 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3648 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3649 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3650 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3651 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3652 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
cristy4c08aed2011-07-01 19:47:50 +00003653 GetPixelIntensity(image,s2)+
cristyed231572011-07-14 02:18:59 +00003654 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3655 GetPixelIntensity(image,s0-GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003656 GetPixelIntensity(image,s0)-
cristyed231572011-07-14 02:18:59 +00003657 GetPixelIntensity(image,s0+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003658 if ((normal.x == 0.0) && (normal.y == 0.0))
3659 shade=light.z;
3660 else
3661 {
3662 shade=0.0;
3663 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3664 if (distance > MagickEpsilon)
3665 {
3666 normal_distance=
3667 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3668 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3669 shade=distance/sqrt((double) normal_distance);
3670 }
3671 }
3672 if (gray != MagickFalse)
3673 {
cristy4c08aed2011-07-01 19:47:50 +00003674 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3675 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3676 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
cristy3ed852e2009-09-05 21:47:34 +00003677 }
3678 else
3679 {
cristy4c08aed2011-07-01 19:47:50 +00003680 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3681 GetPixelRed(image,s1)),q);
3682 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3683 GetPixelGreen(image,s1)),q);
3684 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3685 GetPixelBlue(image,s1)),q);
cristy3ed852e2009-09-05 21:47:34 +00003686 }
cristy4c08aed2011-07-01 19:47:50 +00003687 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
cristyed231572011-07-14 02:18:59 +00003688 s0+=GetPixelChannels(image);
3689 s1+=GetPixelChannels(image);
3690 s2+=GetPixelChannels(image);
3691 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003692 }
3693 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3694 status=MagickFalse;
3695 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3696 {
3697 MagickBooleanType
3698 proceed;
3699
cristyb5d5f722009-11-04 03:03:49 +00003700#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003701 #pragma omp critical (MagickCore_ShadeImage)
3702#endif
3703 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3704 if (proceed == MagickFalse)
3705 status=MagickFalse;
3706 }
3707 }
3708 shade_view=DestroyCacheView(shade_view);
3709 image_view=DestroyCacheView(image_view);
3710 if (status == MagickFalse)
3711 shade_image=DestroyImage(shade_image);
3712 return(shade_image);
3713}
3714
3715/*
3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3717% %
3718% %
3719% %
3720% S h a r p e n I m a g e %
3721% %
3722% %
3723% %
3724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3725%
3726% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3727% operator of the given radius and standard deviation (sigma). For
3728% reasonable results, radius should be larger than sigma. Use a radius of 0
3729% and SharpenImage() selects a suitable radius for you.
3730%
3731% Using a separable kernel would be faster, but the negative weights cancel
3732% out on the corners of the kernel producing often undesirable ringing in the
3733% filtered result; this can be avoided by using a 2D gaussian shaped image
3734% sharpening kernel instead.
3735%
3736% The format of the SharpenImage method is:
3737%
3738% Image *SharpenImage(const Image *image,const double radius,
3739% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003740%
3741% A description of each parameter follows:
3742%
3743% o image: the image.
3744%
cristy3ed852e2009-09-05 21:47:34 +00003745% o radius: the radius of the Gaussian, in pixels, not counting the center
3746% pixel.
3747%
3748% o sigma: the standard deviation of the Laplacian, in pixels.
3749%
3750% o exception: return any errors or warnings in this structure.
3751%
3752*/
cristy3ed852e2009-09-05 21:47:34 +00003753MagickExport Image *SharpenImage(const Image *image,const double radius,
3754 const double sigma,ExceptionInfo *exception)
3755{
cristy3ed852e2009-09-05 21:47:34 +00003756 double
cristy47e00502009-12-17 19:19:57 +00003757 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003758
3759 Image
3760 *sharp_image;
3761
cristy41cbe682011-07-15 19:12:37 +00003762 KernelInfo
3763 *kernel_info;
3764
cristybb503372010-05-27 20:51:26 +00003765 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003766 i;
3767
cristybb503372010-05-27 20:51:26 +00003768 size_t
cristy3ed852e2009-09-05 21:47:34 +00003769 width;
3770
cristy117ff172010-08-15 21:35:32 +00003771 ssize_t
3772 j,
3773 u,
3774 v;
3775
cristy3ed852e2009-09-05 21:47:34 +00003776 assert(image != (const Image *) NULL);
3777 assert(image->signature == MagickSignature);
3778 if (image->debug != MagickFalse)
3779 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3780 assert(exception != (ExceptionInfo *) NULL);
3781 assert(exception->signature == MagickSignature);
3782 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003783 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003784 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003785 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003786 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3787 kernel_info->width=width;
3788 kernel_info->height=width;
3789 kernel_info->signature=MagickSignature;
3790 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3791 kernel_info->width*sizeof(*kernel_info->values));
3792 if (kernel_info->values == (double *) NULL)
3793 {
3794 kernel_info=DestroyKernelInfo(kernel_info);
3795 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3796 }
cristy3ed852e2009-09-05 21:47:34 +00003797 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003798 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003799 i=0;
3800 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003801 {
cristy47e00502009-12-17 19:19:57 +00003802 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003803 {
cristy41cbe682011-07-15 19:12:37 +00003804 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3805 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3806 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003807 i++;
3808 }
3809 }
cristy41cbe682011-07-15 19:12:37 +00003810 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy0a922382011-07-16 15:30:34 +00003811 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00003812 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003813 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003814 return(sharp_image);
3815}
3816
3817/*
3818%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3819% %
3820% %
3821% %
3822% S p r e a d I m a g e %
3823% %
3824% %
3825% %
3826%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3827%
3828% SpreadImage() is a special effects method that randomly displaces each
3829% pixel in a block defined by the radius parameter.
3830%
3831% The format of the SpreadImage method is:
3832%
3833% Image *SpreadImage(const Image *image,const double radius,
3834% ExceptionInfo *exception)
3835%
3836% A description of each parameter follows:
3837%
3838% o image: the image.
3839%
3840% o radius: Choose a random pixel in a neighborhood of this extent.
3841%
3842% o exception: return any errors or warnings in this structure.
3843%
3844*/
3845MagickExport Image *SpreadImage(const Image *image,const double radius,
3846 ExceptionInfo *exception)
3847{
3848#define SpreadImageTag "Spread/Image"
3849
cristyfa112112010-01-04 17:48:07 +00003850 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003851 *image_view,
3852 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003853
cristy3ed852e2009-09-05 21:47:34 +00003854 Image
3855 *spread_image;
3856
cristy3ed852e2009-09-05 21:47:34 +00003857 MagickBooleanType
3858 status;
3859
cristybb503372010-05-27 20:51:26 +00003860 MagickOffsetType
3861 progress;
3862
cristy4c08aed2011-07-01 19:47:50 +00003863 PixelInfo
cristyddd82202009-11-03 20:14:50 +00003864 bias;
cristy3ed852e2009-09-05 21:47:34 +00003865
3866 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003867 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003868
cristybb503372010-05-27 20:51:26 +00003869 size_t
cristy3ed852e2009-09-05 21:47:34 +00003870 width;
3871
cristybb503372010-05-27 20:51:26 +00003872 ssize_t
3873 y;
3874
cristy3ed852e2009-09-05 21:47:34 +00003875 /*
3876 Initialize spread image attributes.
3877 */
3878 assert(image != (Image *) NULL);
3879 assert(image->signature == MagickSignature);
3880 if (image->debug != MagickFalse)
3881 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3882 assert(exception != (ExceptionInfo *) NULL);
3883 assert(exception->signature == MagickSignature);
3884 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3885 exception);
3886 if (spread_image == (Image *) NULL)
3887 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003888 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003889 {
cristy3ed852e2009-09-05 21:47:34 +00003890 spread_image=DestroyImage(spread_image);
3891 return((Image *) NULL);
3892 }
3893 /*
3894 Spread image.
3895 */
3896 status=MagickTrue;
3897 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003898 GetPixelInfo(spread_image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003899 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003900 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003901 image_view=AcquireCacheView(image);
3902 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003903#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00003904 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00003905#endif
cristybb503372010-05-27 20:51:26 +00003906 for (y=0; y < (ssize_t) spread_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003907 {
cristy5c9e6f22010-09-17 17:31:01 +00003908 const int
3909 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003910
cristy4c08aed2011-07-01 19:47:50 +00003911 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003912 pixel;
3913
cristy4c08aed2011-07-01 19:47:50 +00003914 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003915 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003916
cristy117ff172010-08-15 21:35:32 +00003917 register ssize_t
3918 x;
3919
cristy3ed852e2009-09-05 21:47:34 +00003920 if (status == MagickFalse)
3921 continue;
cristy9f7e7cb2011-03-26 00:49:57 +00003922 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003923 exception);
cristyacd2ed22011-08-30 01:44:23 +00003924 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003925 {
3926 status=MagickFalse;
3927 continue;
3928 }
cristyddd82202009-11-03 20:14:50 +00003929 pixel=bias;
cristybb503372010-05-27 20:51:26 +00003930 for (x=0; x < (ssize_t) spread_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003931 {
cristy4c08aed2011-07-01 19:47:50 +00003932 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003933 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3934 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3935 random_info[id])-0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003936 SetPixelPixelInfo(spread_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003937 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003938 }
cristy9f7e7cb2011-03-26 00:49:57 +00003939 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003940 status=MagickFalse;
3941 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3942 {
3943 MagickBooleanType
3944 proceed;
3945
cristyb557a152011-02-22 12:14:30 +00003946#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003947 #pragma omp critical (MagickCore_SpreadImage)
3948#endif
3949 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3950 if (proceed == MagickFalse)
3951 status=MagickFalse;
3952 }
3953 }
cristy9f7e7cb2011-03-26 00:49:57 +00003954 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003955 image_view=DestroyCacheView(image_view);
3956 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003957 return(spread_image);
3958}
3959
3960/*
3961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3962% %
3963% %
3964% %
cristy0834d642011-03-18 18:26:08 +00003965% S t a t i s t i c I m a g e %
3966% %
3967% %
3968% %
3969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3970%
3971% StatisticImage() makes each pixel the min / max / median / mode / etc. of
cristy8d752042011-03-19 01:00:36 +00003972% the neighborhood of the specified width and height.
cristy0834d642011-03-18 18:26:08 +00003973%
3974% The format of the StatisticImage method is:
3975%
3976% Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00003977% const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00003978%
3979% A description of each parameter follows:
3980%
3981% o image: the image.
3982%
cristy0834d642011-03-18 18:26:08 +00003983% o type: the statistic type (median, mode, etc.).
3984%
cristy95c38342011-03-18 22:39:51 +00003985% o width: the width of the pixel neighborhood.
3986%
3987% o height: the height of the pixel neighborhood.
cristy0834d642011-03-18 18:26:08 +00003988%
3989% o exception: return any errors or warnings in this structure.
3990%
3991*/
3992
cristy733678d2011-03-18 21:29:28 +00003993#define ListChannels 5
3994
3995typedef struct _ListNode
3996{
3997 size_t
3998 next[9],
3999 count,
4000 signature;
4001} ListNode;
4002
4003typedef struct _SkipList
4004{
4005 ssize_t
4006 level;
4007
4008 ListNode
4009 *nodes;
4010} SkipList;
4011
4012typedef struct _PixelList
4013{
4014 size_t
cristy6fc86bb2011-03-18 23:45:16 +00004015 length,
cristy733678d2011-03-18 21:29:28 +00004016 seed,
4017 signature;
4018
4019 SkipList
4020 lists[ListChannels];
4021} PixelList;
4022
4023static PixelList *DestroyPixelList(PixelList *pixel_list)
4024{
4025 register ssize_t
4026 i;
4027
4028 if (pixel_list == (PixelList *) NULL)
4029 return((PixelList *) NULL);
4030 for (i=0; i < ListChannels; i++)
4031 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
4032 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
4033 pixel_list->lists[i].nodes);
4034 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
4035 return(pixel_list);
4036}
4037
4038static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
4039{
4040 register ssize_t
4041 i;
4042
4043 assert(pixel_list != (PixelList **) NULL);
4044 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
4045 if (pixel_list[i] != (PixelList *) NULL)
4046 pixel_list[i]=DestroyPixelList(pixel_list[i]);
4047 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
4048 return(pixel_list);
4049}
4050
cristy6fc86bb2011-03-18 23:45:16 +00004051static PixelList *AcquirePixelList(const size_t width,const size_t height)
cristy733678d2011-03-18 21:29:28 +00004052{
4053 PixelList
4054 *pixel_list;
4055
4056 register ssize_t
4057 i;
4058
4059 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
4060 if (pixel_list == (PixelList *) NULL)
4061 return(pixel_list);
4062 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
cristy6fc86bb2011-03-18 23:45:16 +00004063 pixel_list->length=width*height;
cristy733678d2011-03-18 21:29:28 +00004064 for (i=0; i < ListChannels; i++)
4065 {
4066 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4067 sizeof(*pixel_list->lists[i].nodes));
4068 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4069 return(DestroyPixelList(pixel_list));
4070 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4071 sizeof(*pixel_list->lists[i].nodes));
4072 }
4073 pixel_list->signature=MagickSignature;
4074 return(pixel_list);
4075}
4076
cristy6fc86bb2011-03-18 23:45:16 +00004077static PixelList **AcquirePixelListThreadSet(const size_t width,
4078 const size_t height)
cristy733678d2011-03-18 21:29:28 +00004079{
4080 PixelList
4081 **pixel_list;
4082
4083 register ssize_t
4084 i;
4085
4086 size_t
4087 number_threads;
4088
4089 number_threads=GetOpenMPMaximumThreads();
4090 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4091 sizeof(*pixel_list));
4092 if (pixel_list == (PixelList **) NULL)
4093 return((PixelList **) NULL);
4094 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4095 for (i=0; i < (ssize_t) number_threads; i++)
4096 {
cristy6fc86bb2011-03-18 23:45:16 +00004097 pixel_list[i]=AcquirePixelList(width,height);
cristy733678d2011-03-18 21:29:28 +00004098 if (pixel_list[i] == (PixelList *) NULL)
4099 return(DestroyPixelListThreadSet(pixel_list));
4100 }
4101 return(pixel_list);
4102}
4103
4104static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4105 const size_t color)
4106{
4107 register SkipList
4108 *list;
4109
4110 register ssize_t
4111 level;
4112
4113 size_t
4114 search,
4115 update[9];
4116
4117 /*
4118 Initialize the node.
4119 */
4120 list=pixel_list->lists+channel;
4121 list->nodes[color].signature=pixel_list->signature;
4122 list->nodes[color].count=1;
4123 /*
4124 Determine where it belongs in the list.
4125 */
4126 search=65536UL;
4127 for (level=list->level; level >= 0; level--)
4128 {
4129 while (list->nodes[search].next[level] < color)
4130 search=list->nodes[search].next[level];
4131 update[level]=search;
4132 }
4133 /*
4134 Generate a pseudo-random level for this node.
4135 */
4136 for (level=0; ; level++)
4137 {
4138 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4139 if ((pixel_list->seed & 0x300) != 0x300)
4140 break;
4141 }
4142 if (level > 8)
4143 level=8;
4144 if (level > (list->level+2))
4145 level=list->level+2;
4146 /*
4147 If we're raising the list's level, link back to the root node.
4148 */
4149 while (level > list->level)
4150 {
4151 list->level++;
4152 update[list->level]=65536UL;
4153 }
4154 /*
4155 Link the node into the skip-list.
4156 */
4157 do
4158 {
4159 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4160 list->nodes[update[level]].next[level]=color;
cristy3cba8ca2011-03-19 01:29:12 +00004161 } while (level-- > 0);
cristy733678d2011-03-18 21:29:28 +00004162}
4163
cristy4c08aed2011-07-01 19:47:50 +00004164static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004165{
cristy4c08aed2011-07-01 19:47:50 +00004166 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004167 pixel;
4168
4169 register SkipList
4170 *list;
4171
4172 register ssize_t
4173 channel;
4174
4175 size_t
cristyd76c51e2011-03-26 00:21:26 +00004176 color,
4177 maximum;
cristy49f37242011-03-22 18:18:23 +00004178
4179 ssize_t
cristy6fc86bb2011-03-18 23:45:16 +00004180 count;
4181
4182 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004183 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004184
4185 /*
4186 Find the maximum value for each of the color.
4187 */
4188 for (channel=0; channel < 5; channel++)
4189 {
4190 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004191 color=65536L;
cristy6fc86bb2011-03-18 23:45:16 +00004192 count=0;
cristy49f37242011-03-22 18:18:23 +00004193 maximum=list->nodes[color].next[0];
cristy6fc86bb2011-03-18 23:45:16 +00004194 do
4195 {
4196 color=list->nodes[color].next[0];
cristy49f37242011-03-22 18:18:23 +00004197 if (color > maximum)
4198 maximum=color;
cristy6fc86bb2011-03-18 23:45:16 +00004199 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004200 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004201 channels[channel]=(unsigned short) maximum;
4202 }
cristy4c08aed2011-07-01 19:47:50 +00004203 GetPixelInfo((const Image *) NULL,&pixel);
cristy49f37242011-03-22 18:18:23 +00004204 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4205 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4206 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004207 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4208 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy49f37242011-03-22 18:18:23 +00004209 return(pixel);
4210}
4211
cristy4c08aed2011-07-01 19:47:50 +00004212static PixelInfo GetMeanPixelList(PixelList *pixel_list)
cristy49f37242011-03-22 18:18:23 +00004213{
cristy4c08aed2011-07-01 19:47:50 +00004214 PixelInfo
cristy49f37242011-03-22 18:18:23 +00004215 pixel;
4216
cristy80a99a32011-03-30 01:30:23 +00004217 MagickRealType
4218 sum;
4219
cristy49f37242011-03-22 18:18:23 +00004220 register SkipList
4221 *list;
4222
4223 register ssize_t
4224 channel;
4225
4226 size_t
cristy80a99a32011-03-30 01:30:23 +00004227 color;
cristy49f37242011-03-22 18:18:23 +00004228
4229 ssize_t
4230 count;
4231
4232 unsigned short
4233 channels[ListChannels];
4234
4235 /*
4236 Find the mean value for each of the color.
4237 */
4238 for (channel=0; channel < 5; channel++)
4239 {
4240 list=pixel_list->lists+channel;
4241 color=65536L;
4242 count=0;
cristy80a99a32011-03-30 01:30:23 +00004243 sum=0.0;
cristy49f37242011-03-22 18:18:23 +00004244 do
4245 {
4246 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004247 sum+=(MagickRealType) list->nodes[color].count*color;
cristy49f37242011-03-22 18:18:23 +00004248 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004249 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004250 sum/=pixel_list->length;
4251 channels[channel]=(unsigned short) sum;
cristy6fc86bb2011-03-18 23:45:16 +00004252 }
cristy4c08aed2011-07-01 19:47:50 +00004253 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004254 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4255 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4256 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004257 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4258 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004259 return(pixel);
4260}
4261
cristy4c08aed2011-07-01 19:47:50 +00004262static PixelInfo GetMedianPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004263{
cristy4c08aed2011-07-01 19:47:50 +00004264 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004265 pixel;
4266
4267 register SkipList
4268 *list;
4269
4270 register ssize_t
4271 channel;
4272
4273 size_t
cristy49f37242011-03-22 18:18:23 +00004274 color;
4275
4276 ssize_t
cristy733678d2011-03-18 21:29:28 +00004277 count;
4278
4279 unsigned short
4280 channels[ListChannels];
4281
4282 /*
4283 Find the median value for each of the color.
4284 */
cristy733678d2011-03-18 21:29:28 +00004285 for (channel=0; channel < 5; channel++)
4286 {
4287 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004288 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004289 count=0;
4290 do
4291 {
4292 color=list->nodes[color].next[0];
4293 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004294 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy6fc86bb2011-03-18 23:45:16 +00004295 channels[channel]=(unsigned short) color;
4296 }
cristy4c08aed2011-07-01 19:47:50 +00004297 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004298 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4299 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4300 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004301 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4302 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004303 return(pixel);
4304}
4305
cristy4c08aed2011-07-01 19:47:50 +00004306static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004307{
cristy4c08aed2011-07-01 19:47:50 +00004308 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004309 pixel;
4310
4311 register SkipList
4312 *list;
4313
4314 register ssize_t
4315 channel;
4316
4317 size_t
cristyd76c51e2011-03-26 00:21:26 +00004318 color,
4319 minimum;
cristy6fc86bb2011-03-18 23:45:16 +00004320
cristy49f37242011-03-22 18:18:23 +00004321 ssize_t
4322 count;
4323
cristy6fc86bb2011-03-18 23:45:16 +00004324 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004325 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004326
4327 /*
4328 Find the minimum value for each of the color.
4329 */
4330 for (channel=0; channel < 5; channel++)
4331 {
4332 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004333 count=0;
cristy6fc86bb2011-03-18 23:45:16 +00004334 color=65536UL;
cristy49f37242011-03-22 18:18:23 +00004335 minimum=list->nodes[color].next[0];
4336 do
4337 {
4338 color=list->nodes[color].next[0];
4339 if (color < minimum)
4340 minimum=color;
4341 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004342 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004343 channels[channel]=(unsigned short) minimum;
cristy733678d2011-03-18 21:29:28 +00004344 }
cristy4c08aed2011-07-01 19:47:50 +00004345 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004346 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4347 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4348 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004349 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4350 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004351 return(pixel);
4352}
4353
cristy4c08aed2011-07-01 19:47:50 +00004354static PixelInfo GetModePixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004355{
cristy4c08aed2011-07-01 19:47:50 +00004356 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004357 pixel;
4358
4359 register SkipList
4360 *list;
4361
4362 register ssize_t
4363 channel;
4364
4365 size_t
4366 color,
cristy733678d2011-03-18 21:29:28 +00004367 max_count,
cristy6fc86bb2011-03-18 23:45:16 +00004368 mode;
cristy733678d2011-03-18 21:29:28 +00004369
cristy49f37242011-03-22 18:18:23 +00004370 ssize_t
4371 count;
4372
cristy733678d2011-03-18 21:29:28 +00004373 unsigned short
4374 channels[5];
4375
4376 /*
glennrp30d2dc62011-06-25 03:17:16 +00004377 Make each pixel the 'predominant color' of the specified neighborhood.
cristy733678d2011-03-18 21:29:28 +00004378 */
cristy733678d2011-03-18 21:29:28 +00004379 for (channel=0; channel < 5; channel++)
4380 {
4381 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004382 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004383 mode=color;
4384 max_count=list->nodes[mode].count;
4385 count=0;
4386 do
4387 {
4388 color=list->nodes[color].next[0];
4389 if (list->nodes[color].count > max_count)
4390 {
4391 mode=color;
4392 max_count=list->nodes[mode].count;
4393 }
4394 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004395 } while (count < (ssize_t) pixel_list->length);
cristy733678d2011-03-18 21:29:28 +00004396 channels[channel]=(unsigned short) mode;
4397 }
cristy4c08aed2011-07-01 19:47:50 +00004398 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004399 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4400 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4401 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004402 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4403 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004404 return(pixel);
4405}
4406
cristy4c08aed2011-07-01 19:47:50 +00004407static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004408{
cristy4c08aed2011-07-01 19:47:50 +00004409 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004410 pixel;
4411
4412 register SkipList
4413 *list;
4414
4415 register ssize_t
4416 channel;
4417
4418 size_t
cristy733678d2011-03-18 21:29:28 +00004419 color,
cristy733678d2011-03-18 21:29:28 +00004420 next,
4421 previous;
4422
cristy49f37242011-03-22 18:18:23 +00004423 ssize_t
4424 count;
4425
cristy733678d2011-03-18 21:29:28 +00004426 unsigned short
4427 channels[5];
4428
4429 /*
cristy49f37242011-03-22 18:18:23 +00004430 Finds the non peak value for each of the colors.
cristy733678d2011-03-18 21:29:28 +00004431 */
cristy733678d2011-03-18 21:29:28 +00004432 for (channel=0; channel < 5; channel++)
4433 {
4434 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004435 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004436 next=list->nodes[color].next[0];
4437 count=0;
4438 do
4439 {
4440 previous=color;
4441 color=next;
4442 next=list->nodes[color].next[0];
4443 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004444 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy733678d2011-03-18 21:29:28 +00004445 if ((previous == 65536UL) && (next != 65536UL))
4446 color=next;
4447 else
4448 if ((previous != 65536UL) && (next == 65536UL))
4449 color=previous;
4450 channels[channel]=(unsigned short) color;
4451 }
cristy4c08aed2011-07-01 19:47:50 +00004452 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004453 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4454 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4455 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004456 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4457 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy733678d2011-03-18 21:29:28 +00004458 return(pixel);
4459}
4460
cristy4c08aed2011-07-01 19:47:50 +00004461static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
cristy9a68cbb2011-03-29 00:51:23 +00004462{
cristy4c08aed2011-07-01 19:47:50 +00004463 PixelInfo
cristy9a68cbb2011-03-29 00:51:23 +00004464 pixel;
4465
cristy80a99a32011-03-30 01:30:23 +00004466 MagickRealType
4467 sum,
4468 sum_squared;
4469
cristy9a68cbb2011-03-29 00:51:23 +00004470 register SkipList
4471 *list;
4472
4473 register ssize_t
4474 channel;
4475
4476 size_t
cristy80a99a32011-03-30 01:30:23 +00004477 color;
cristy9a68cbb2011-03-29 00:51:23 +00004478
4479 ssize_t
4480 count;
4481
4482 unsigned short
4483 channels[ListChannels];
4484
4485 /*
cristy80a99a32011-03-30 01:30:23 +00004486 Find the standard-deviation value for each of the color.
cristy9a68cbb2011-03-29 00:51:23 +00004487 */
4488 for (channel=0; channel < 5; channel++)
4489 {
4490 list=pixel_list->lists+channel;
4491 color=65536L;
4492 count=0;
cristy80a99a32011-03-30 01:30:23 +00004493 sum=0.0;
4494 sum_squared=0.0;
cristy9a68cbb2011-03-29 00:51:23 +00004495 do
4496 {
cristy80a99a32011-03-30 01:30:23 +00004497 register ssize_t
4498 i;
4499
cristy9a68cbb2011-03-29 00:51:23 +00004500 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004501 sum+=(MagickRealType) list->nodes[color].count*color;
4502 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4503 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
cristy9a68cbb2011-03-29 00:51:23 +00004504 count+=list->nodes[color].count;
4505 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004506 sum/=pixel_list->length;
4507 sum_squared/=pixel_list->length;
4508 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
cristy9a68cbb2011-03-29 00:51:23 +00004509 }
cristy4c08aed2011-07-01 19:47:50 +00004510 GetPixelInfo((const Image *) NULL,&pixel);
cristy9a68cbb2011-03-29 00:51:23 +00004511 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4512 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4513 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004514 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4515 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy9a68cbb2011-03-29 00:51:23 +00004516 return(pixel);
4517}
4518
cristy4c08aed2011-07-01 19:47:50 +00004519static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4520 PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004521{
4522 size_t
4523 signature;
4524
4525 unsigned short
4526 index;
4527
cristy4c08aed2011-07-01 19:47:50 +00004528 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004529 signature=pixel_list->lists[0].nodes[index].signature;
4530 if (signature == pixel_list->signature)
4531 pixel_list->lists[0].nodes[index].count++;
4532 else
4533 AddNodePixelList(pixel_list,0,index);
cristy4c08aed2011-07-01 19:47:50 +00004534 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004535 signature=pixel_list->lists[1].nodes[index].signature;
4536 if (signature == pixel_list->signature)
4537 pixel_list->lists[1].nodes[index].count++;
4538 else
4539 AddNodePixelList(pixel_list,1,index);
cristy4c08aed2011-07-01 19:47:50 +00004540 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004541 signature=pixel_list->lists[2].nodes[index].signature;
4542 if (signature == pixel_list->signature)
4543 pixel_list->lists[2].nodes[index].count++;
4544 else
4545 AddNodePixelList(pixel_list,2,index);
cristy4c08aed2011-07-01 19:47:50 +00004546 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004547 signature=pixel_list->lists[3].nodes[index].signature;
4548 if (signature == pixel_list->signature)
4549 pixel_list->lists[3].nodes[index].count++;
4550 else
4551 AddNodePixelList(pixel_list,3,index);
4552 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004553 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004554 signature=pixel_list->lists[4].nodes[index].signature;
4555 if (signature == pixel_list->signature)
4556 pixel_list->lists[4].nodes[index].count++;
4557 else
4558 AddNodePixelList(pixel_list,4,index);
4559}
4560
cristy80c99742011-04-04 14:46:39 +00004561static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4562{
4563 if (x < 0)
4564 return(-x);
4565 return(x);
4566}
4567
cristy733678d2011-03-18 21:29:28 +00004568static void ResetPixelList(PixelList *pixel_list)
4569{
4570 int
4571 level;
4572
4573 register ListNode
4574 *root;
4575
4576 register SkipList
4577 *list;
4578
4579 register ssize_t
4580 channel;
4581
4582 /*
4583 Reset the skip-list.
4584 */
4585 for (channel=0; channel < 5; channel++)
4586 {
4587 list=pixel_list->lists+channel;
4588 root=list->nodes+65536UL;
4589 list->level=0;
4590 for (level=0; level < 9; level++)
4591 root->next[level]=65536UL;
4592 }
4593 pixel_list->seed=pixel_list->signature++;
4594}
4595
cristy0834d642011-03-18 18:26:08 +00004596MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00004597 const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00004598{
cristy3cba8ca2011-03-19 01:29:12 +00004599#define StatisticWidth \
cristyd76c51e2011-03-26 00:21:26 +00004600 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
cristy3cba8ca2011-03-19 01:29:12 +00004601#define StatisticHeight \
cristyd76c51e2011-03-26 00:21:26 +00004602 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
cristy0834d642011-03-18 18:26:08 +00004603#define StatisticImageTag "Statistic/Image"
4604
4605 CacheView
4606 *image_view,
4607 *statistic_view;
4608
4609 Image
4610 *statistic_image;
4611
4612 MagickBooleanType
4613 status;
4614
4615 MagickOffsetType
4616 progress;
4617
4618 PixelList
4619 **restrict pixel_list;
4620
cristy0834d642011-03-18 18:26:08 +00004621 ssize_t
4622 y;
4623
4624 /*
4625 Initialize statistics image attributes.
4626 */
4627 assert(image != (Image *) NULL);
4628 assert(image->signature == MagickSignature);
4629 if (image->debug != MagickFalse)
4630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4631 assert(exception != (ExceptionInfo *) NULL);
4632 assert(exception->signature == MagickSignature);
cristy0834d642011-03-18 18:26:08 +00004633 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4634 exception);
4635 if (statistic_image == (Image *) NULL)
4636 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00004637 if (SetImageStorageClass(statistic_image,DirectClass,exception) == MagickFalse)
cristy0834d642011-03-18 18:26:08 +00004638 {
cristy0834d642011-03-18 18:26:08 +00004639 statistic_image=DestroyImage(statistic_image);
4640 return((Image *) NULL);
4641 }
cristy6fc86bb2011-03-18 23:45:16 +00004642 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
cristy0834d642011-03-18 18:26:08 +00004643 if (pixel_list == (PixelList **) NULL)
4644 {
4645 statistic_image=DestroyImage(statistic_image);
4646 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4647 }
4648 /*
cristy8d752042011-03-19 01:00:36 +00004649 Make each pixel the min / max / median / mode / etc. of the neighborhood.
cristy0834d642011-03-18 18:26:08 +00004650 */
4651 status=MagickTrue;
4652 progress=0;
4653 image_view=AcquireCacheView(image);
4654 statistic_view=AcquireCacheView(statistic_image);
4655#if defined(MAGICKCORE_OPENMP_SUPPORT)
4656 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4657#endif
4658 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4659 {
4660 const int
4661 id = GetOpenMPThreadId();
4662
cristy4c08aed2011-07-01 19:47:50 +00004663 register const Quantum
cristy0834d642011-03-18 18:26:08 +00004664 *restrict p;
4665
cristy4c08aed2011-07-01 19:47:50 +00004666 register Quantum
cristy0834d642011-03-18 18:26:08 +00004667 *restrict q;
4668
4669 register ssize_t
4670 x;
4671
4672 if (status == MagickFalse)
4673 continue;
cristy6fc86bb2011-03-18 23:45:16 +00004674 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4675 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4676 StatisticHeight,exception);
cristy3cba8ca2011-03-19 01:29:12 +00004677 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004678 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy0834d642011-03-18 18:26:08 +00004679 {
4680 status=MagickFalse;
4681 continue;
4682 }
cristy0834d642011-03-18 18:26:08 +00004683 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4684 {
cristy4c08aed2011-07-01 19:47:50 +00004685 PixelInfo
cristy0834d642011-03-18 18:26:08 +00004686 pixel;
4687
cristy4c08aed2011-07-01 19:47:50 +00004688 register const Quantum
cristy6e3026a2011-03-19 00:54:38 +00004689 *restrict r;
4690
cristy0834d642011-03-18 18:26:08 +00004691 register ssize_t
4692 u,
4693 v;
4694
4695 r=p;
cristy0834d642011-03-18 18:26:08 +00004696 ResetPixelList(pixel_list[id]);
cristy6e4c3292011-03-19 00:53:55 +00004697 for (v=0; v < (ssize_t) StatisticHeight; v++)
cristy0834d642011-03-18 18:26:08 +00004698 {
cristy6e4c3292011-03-19 00:53:55 +00004699 for (u=0; u < (ssize_t) StatisticWidth; u++)
cristyed231572011-07-14 02:18:59 +00004700 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4701 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
cristy0834d642011-03-18 18:26:08 +00004702 }
cristy4c08aed2011-07-01 19:47:50 +00004703 GetPixelInfo(image,&pixel);
cristy490408a2011-07-07 14:42:05 +00004704 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
cristyed231572011-07-14 02:18:59 +00004705 GetPixelChannels(image),&pixel);
cristy0834d642011-03-18 18:26:08 +00004706 switch (type)
4707 {
cristy80c99742011-04-04 14:46:39 +00004708 case GradientStatistic:
4709 {
cristy4c08aed2011-07-01 19:47:50 +00004710 PixelInfo
cristy80c99742011-04-04 14:46:39 +00004711 maximum,
4712 minimum;
4713
4714 minimum=GetMinimumPixelList(pixel_list[id]);
4715 maximum=GetMaximumPixelList(pixel_list[id]);
4716 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4717 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4718 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
cristy4c08aed2011-07-01 19:47:50 +00004719 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
cristy80c99742011-04-04 14:46:39 +00004720 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004721 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
cristy80c99742011-04-04 14:46:39 +00004722 break;
4723 }
cristy6fc86bb2011-03-18 23:45:16 +00004724 case MaximumStatistic:
4725 {
4726 pixel=GetMaximumPixelList(pixel_list[id]);
4727 break;
4728 }
cristy49f37242011-03-22 18:18:23 +00004729 case MeanStatistic:
4730 {
4731 pixel=GetMeanPixelList(pixel_list[id]);
4732 break;
4733 }
cristyf2ad14a2011-03-18 18:57:25 +00004734 case MedianStatistic:
cristy6fc86bb2011-03-18 23:45:16 +00004735 default:
cristyf2ad14a2011-03-18 18:57:25 +00004736 {
4737 pixel=GetMedianPixelList(pixel_list[id]);
4738 break;
4739 }
cristy6fc86bb2011-03-18 23:45:16 +00004740 case MinimumStatistic:
4741 {
4742 pixel=GetMinimumPixelList(pixel_list[id]);
4743 break;
4744 }
cristyf2ad14a2011-03-18 18:57:25 +00004745 case ModeStatistic:
4746 {
4747 pixel=GetModePixelList(pixel_list[id]);
4748 break;
4749 }
4750 case NonpeakStatistic:
4751 {
4752 pixel=GetNonpeakPixelList(pixel_list[id]);
4753 break;
4754 }
cristy9a68cbb2011-03-29 00:51:23 +00004755 case StandardDeviationStatistic:
4756 {
4757 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4758 break;
4759 }
cristy0834d642011-03-18 18:26:08 +00004760 }
cristyed231572011-07-14 02:18:59 +00004761 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004762 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00004763 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004764 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00004765 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004766 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00004767 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy0834d642011-03-18 18:26:08 +00004768 (image->colorspace == CMYKColorspace))
cristy490408a2011-07-07 14:42:05 +00004769 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00004770 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00004771 (image->matte != MagickFalse))
cristy490408a2011-07-07 14:42:05 +00004772 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004773 p+=GetPixelChannels(image);
4774 q+=GetPixelChannels(statistic_image);
cristy0834d642011-03-18 18:26:08 +00004775 }
4776 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4777 status=MagickFalse;
4778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4779 {
4780 MagickBooleanType
4781 proceed;
4782
4783#if defined(MAGICKCORE_OPENMP_SUPPORT)
4784 #pragma omp critical (MagickCore_StatisticImage)
4785#endif
4786 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4787 image->rows);
4788 if (proceed == MagickFalse)
4789 status=MagickFalse;
4790 }
4791 }
4792 statistic_view=DestroyCacheView(statistic_view);
4793 image_view=DestroyCacheView(image_view);
4794 pixel_list=DestroyPixelListThreadSet(pixel_list);
4795 return(statistic_image);
4796}
4797
4798/*
4799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4800% %
4801% %
4802% %
cristy3ed852e2009-09-05 21:47:34 +00004803% U n s h a r p M a s k I m a g e %
4804% %
4805% %
4806% %
4807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4808%
4809% UnsharpMaskImage() sharpens one or more image channels. We convolve the
4810% image with a Gaussian operator of the given radius and standard deviation
4811% (sigma). For reasonable results, radius should be larger than sigma. Use a
4812% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4813%
4814% The format of the UnsharpMaskImage method is:
4815%
4816% Image *UnsharpMaskImage(const Image *image,const double radius,
4817% const double sigma,const double amount,const double threshold,
4818% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004819%
4820% A description of each parameter follows:
4821%
4822% o image: the image.
4823%
cristy3ed852e2009-09-05 21:47:34 +00004824% o radius: the radius of the Gaussian, in pixels, not counting the center
4825% pixel.
4826%
4827% o sigma: the standard deviation of the Gaussian, in pixels.
4828%
4829% o amount: the percentage of the difference between the original and the
4830% blur image that is added back into the original.
4831%
4832% o threshold: the threshold in pixels needed to apply the diffence amount.
4833%
4834% o exception: return any errors or warnings in this structure.
4835%
4836*/
cristyf4ad9df2011-07-08 16:49:03 +00004837MagickExport Image *UnsharpMaskImage(const Image *image,
4838 const double radius,const double sigma,const double amount,
4839 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004840{
4841#define SharpenImageTag "Sharpen/Image"
4842
cristyc4c8d132010-01-07 01:58:38 +00004843 CacheView
4844 *image_view,
4845 *unsharp_view;
4846
cristy3ed852e2009-09-05 21:47:34 +00004847 Image
4848 *unsharp_image;
4849
cristy3ed852e2009-09-05 21:47:34 +00004850 MagickBooleanType
4851 status;
4852
cristybb503372010-05-27 20:51:26 +00004853 MagickOffsetType
4854 progress;
4855
cristy4c08aed2011-07-01 19:47:50 +00004856 PixelInfo
cristyddd82202009-11-03 20:14:50 +00004857 bias;
cristy3ed852e2009-09-05 21:47:34 +00004858
4859 MagickRealType
4860 quantum_threshold;
4861
cristybb503372010-05-27 20:51:26 +00004862 ssize_t
4863 y;
4864
cristy3ed852e2009-09-05 21:47:34 +00004865 assert(image != (const Image *) NULL);
4866 assert(image->signature == MagickSignature);
4867 if (image->debug != MagickFalse)
4868 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4869 assert(exception != (ExceptionInfo *) NULL);
cristyf4ad9df2011-07-08 16:49:03 +00004870 unsharp_image=BlurImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004871 if (unsharp_image == (Image *) NULL)
4872 return((Image *) NULL);
4873 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4874 /*
4875 Unsharp-mask image.
4876 */
4877 status=MagickTrue;
4878 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004879 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00004880 image_view=AcquireCacheView(image);
4881 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00004882#if defined(MAGICKCORE_OPENMP_SUPPORT)
4883 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004884#endif
cristybb503372010-05-27 20:51:26 +00004885 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004886 {
cristy4c08aed2011-07-01 19:47:50 +00004887 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004888 pixel;
4889
cristy4c08aed2011-07-01 19:47:50 +00004890 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004891 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004892
cristy4c08aed2011-07-01 19:47:50 +00004893 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004894 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004895
cristy117ff172010-08-15 21:35:32 +00004896 register ssize_t
4897 x;
4898
cristy3ed852e2009-09-05 21:47:34 +00004899 if (status == MagickFalse)
4900 continue;
4901 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4902 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4903 exception);
cristy4c08aed2011-07-01 19:47:50 +00004904 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004905 {
4906 status=MagickFalse;
4907 continue;
4908 }
cristyddd82202009-11-03 20:14:50 +00004909 pixel=bias;
cristybb503372010-05-27 20:51:26 +00004910 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004911 {
cristyed231572011-07-14 02:18:59 +00004912 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004913 {
cristy4c08aed2011-07-01 19:47:50 +00004914 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004915 if (fabs(2.0*pixel.red) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004916 pixel.red=(MagickRealType) GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004917 else
cristy4c08aed2011-07-01 19:47:50 +00004918 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4919 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
cristy3ed852e2009-09-05 21:47:34 +00004920 }
cristyed231572011-07-14 02:18:59 +00004921 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004922 {
cristy4c08aed2011-07-01 19:47:50 +00004923 pixel.green=GetPixelGreen(image,p)-
4924 (MagickRealType) GetPixelGreen(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004925 if (fabs(2.0*pixel.green) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004926 pixel.green=(MagickRealType)
4927 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004928 else
cristy4c08aed2011-07-01 19:47:50 +00004929 pixel.green=(MagickRealType)
4930 GetPixelGreen(image,p)+
4931 (pixel.green*amount);
4932 SetPixelGreen(unsharp_image,
4933 ClampToQuantum(pixel.green),q);
cristy3ed852e2009-09-05 21:47:34 +00004934 }
cristyed231572011-07-14 02:18:59 +00004935 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004936 {
cristy4c08aed2011-07-01 19:47:50 +00004937 pixel.blue=GetPixelBlue(image,p)-
4938 (MagickRealType) GetPixelBlue(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004939 if (fabs(2.0*pixel.blue) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004940 pixel.blue=(MagickRealType)
4941 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004942 else
cristy4c08aed2011-07-01 19:47:50 +00004943 pixel.blue=(MagickRealType)
4944 GetPixelBlue(image,p)+(pixel.blue*amount);
4945 SetPixelBlue(unsharp_image,
4946 ClampToQuantum(pixel.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00004947 }
cristyed231572011-07-14 02:18:59 +00004948 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00004949 (image->colorspace == CMYKColorspace))
4950 {
cristy4c08aed2011-07-01 19:47:50 +00004951 pixel.black=GetPixelBlack(image,p)-
4952 (MagickRealType) GetPixelBlack(image,q);
4953 if (fabs(2.0*pixel.black) < quantum_threshold)
4954 pixel.black=(MagickRealType)
4955 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004956 else
cristy4c08aed2011-07-01 19:47:50 +00004957 pixel.black=(MagickRealType)
4958 GetPixelBlack(image,p)+(pixel.black*
4959 amount);
4960 SetPixelBlack(unsharp_image,
4961 ClampToQuantum(pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00004962 }
cristyed231572011-07-14 02:18:59 +00004963 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004964 {
4965 pixel.alpha=GetPixelAlpha(image,p)-
4966 (MagickRealType) GetPixelAlpha(image,q);
4967 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4968 pixel.alpha=(MagickRealType)
4969 GetPixelAlpha(image,p);
4970 else
4971 pixel.alpha=GetPixelAlpha(image,p)+
4972 (pixel.alpha*amount);
4973 SetPixelAlpha(unsharp_image,
4974 ClampToQuantum(pixel.alpha),q);
4975 }
cristyed231572011-07-14 02:18:59 +00004976 p+=GetPixelChannels(image);
4977 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00004978 }
4979 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4980 status=MagickFalse;
4981 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4982 {
4983 MagickBooleanType
4984 proceed;
4985
cristyb5d5f722009-11-04 03:03:49 +00004986#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00004987 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00004988#endif
4989 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4990 if (proceed == MagickFalse)
4991 status=MagickFalse;
4992 }
4993 }
4994 unsharp_image->type=image->type;
4995 unsharp_view=DestroyCacheView(unsharp_view);
4996 image_view=DestroyCacheView(image_view);
4997 if (status == MagickFalse)
4998 unsharp_image=DestroyImage(unsharp_image);
4999 return(unsharp_image);
5000}