blob: 9cdc3d0d26fe7fe3a238acbde81a47f700c6ecfb [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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"
cristyc53413d2011-11-17 13:04:26 +000052#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000053#include "MagickCore/draw.h"
54#include "MagickCore/enhance.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/effect.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000060#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000061#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/montage.h"
69#include "MagickCore/morphology.h"
70#include "MagickCore/paint.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantize.h"
74#include "MagickCore/quantum.h"
75#include "MagickCore/quantum-private.h"
76#include "MagickCore/random_.h"
77#include "MagickCore/random-private.h"
78#include "MagickCore/resample.h"
79#include "MagickCore/resample-private.h"
80#include "MagickCore/resize.h"
81#include "MagickCore/resource_.h"
82#include "MagickCore/segment.h"
cristy31bbf2f2011-11-17 13:19:37 +000083#include "MagickCore/shear.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/signature-private.h"
cristy99bd5232011-12-07 14:38:20 +000085#include "MagickCore/statistic.h"
cristy4c08aed2011-07-01 19:47:50 +000086#include "MagickCore/string_.h"
87#include "MagickCore/thread-private.h"
88#include "MagickCore/transform.h"
89#include "MagickCore/threshold.h"
cristy3ed852e2009-09-05 21:47:34 +000090
91/*
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93% %
94% %
95% %
96% A d a p t i v e B l u r I m a g e %
97% %
98% %
99% %
100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101%
102% AdaptiveBlurImage() adaptively blurs the image by blurring less
103% intensely near image edges and more intensely far from edges. We blur the
104% image with a Gaussian operator of the given radius and standard deviation
105% (sigma). For reasonable results, radius should be larger than sigma. Use a
106% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
107%
108% The format of the AdaptiveBlurImage method is:
109%
110% Image *AdaptiveBlurImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000111% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000112%
113% A description of each parameter follows:
114%
115% o image: the image.
116%
cristy3ed852e2009-09-05 21:47:34 +0000117% o radius: the radius of the Gaussian, in pixels, not counting the center
118% pixel.
119%
120% o sigma: the standard deviation of the Laplacian, in pixels.
121%
cristy4c11c2b2011-09-05 20:17:07 +0000122% o bias: the bias.
123%
cristy3ed852e2009-09-05 21:47:34 +0000124% o exception: return any errors or warnings in this structure.
125%
126*/
127
cristyf89cb1d2011-07-07 01:24:37 +0000128MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
cristy051718b2011-08-28 22:49:25 +0000129 const char *levels,ExceptionInfo *exception)
cristyf89cb1d2011-07-07 01:24:37 +0000130{
131 double
132 black_point,
133 gamma,
134 white_point;
135
136 GeometryInfo
137 geometry_info;
138
139 MagickBooleanType
140 status;
141
142 MagickStatusType
143 flags;
144
145 /*
146 Parse levels.
147 */
148 if (levels == (char *) NULL)
149 return(MagickFalse);
150 flags=ParseGeometry(levels,&geometry_info);
151 black_point=geometry_info.rho;
152 white_point=(double) QuantumRange;
153 if ((flags & SigmaValue) != 0)
154 white_point=geometry_info.sigma;
155 gamma=1.0;
156 if ((flags & XiValue) != 0)
157 gamma=geometry_info.xi;
158 if ((flags & PercentValue) != 0)
159 {
160 black_point*=(double) image->columns*image->rows/100.0;
161 white_point*=(double) image->columns*image->rows/100.0;
162 }
163 if ((flags & SigmaValue) == 0)
164 white_point=(double) QuantumRange-black_point;
165 if ((flags & AspectValue ) == 0)
cristy7c0a0a42011-08-23 17:57:25 +0000166 status=LevelImage(image,black_point,white_point,gamma,exception);
cristyf89cb1d2011-07-07 01:24:37 +0000167 else
cristy7c0a0a42011-08-23 17:57:25 +0000168 status=LevelizeImage(image,black_point,white_point,gamma,exception);
cristyf89cb1d2011-07-07 01:24:37 +0000169 return(status);
170}
171
cristy4282c702011-11-21 00:01:06 +0000172MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
173 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000174{
175#define AdaptiveBlurImageTag "Convolve/Image"
176#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
177
cristyc4c8d132010-01-07 01:58:38 +0000178 CacheView
179 *blur_view,
180 *edge_view,
181 *image_view;
182
cristy3ed852e2009-09-05 21:47:34 +0000183 double
cristy47e00502009-12-17 19:19:57 +0000184 **kernel,
185 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000186
187 Image
188 *blur_image,
189 *edge_image,
190 *gaussian_image;
191
cristy3ed852e2009-09-05 21:47:34 +0000192 MagickBooleanType
193 status;
194
cristybb503372010-05-27 20:51:26 +0000195 MagickOffsetType
196 progress;
197
cristybb503372010-05-27 20:51:26 +0000198 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000199 i;
cristy3ed852e2009-09-05 21:47:34 +0000200
cristybb503372010-05-27 20:51:26 +0000201 size_t
cristy3ed852e2009-09-05 21:47:34 +0000202 width;
203
cristybb503372010-05-27 20:51:26 +0000204 ssize_t
205 j,
206 k,
207 u,
208 v,
209 y;
210
cristy3ed852e2009-09-05 21:47:34 +0000211 assert(image != (const Image *) NULL);
212 assert(image->signature == MagickSignature);
213 if (image->debug != MagickFalse)
214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
215 assert(exception != (ExceptionInfo *) NULL);
216 assert(exception->signature == MagickSignature);
217 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
218 if (blur_image == (Image *) NULL)
219 return((Image *) NULL);
220 if (fabs(sigma) <= MagickEpsilon)
221 return(blur_image);
cristy574cc262011-08-05 01:23:58 +0000222 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000223 {
cristy3ed852e2009-09-05 21:47:34 +0000224 blur_image=DestroyImage(blur_image);
225 return((Image *) NULL);
226 }
227 /*
228 Edge detect the image brighness channel, level, blur, and level again.
229 */
cristy8ae632d2011-09-05 17:29:53 +0000230 edge_image=EdgeImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000231 if (edge_image == (Image *) NULL)
232 {
233 blur_image=DestroyImage(blur_image);
234 return((Image *) NULL);
235 }
cristy051718b2011-08-28 22:49:25 +0000236 (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
cristyd89705a2012-01-20 02:52:24 +0000237 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000238 if (gaussian_image != (Image *) NULL)
239 {
240 edge_image=DestroyImage(edge_image);
241 edge_image=gaussian_image;
242 }
cristy051718b2011-08-28 22:49:25 +0000243 (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000244 /*
245 Create a set of kernels from maximum (radius,sigma) to minimum.
246 */
247 width=GetOptimalKernelWidth2D(radius,sigma);
cristy219a63a2012-01-17 12:30:33 +0000248 kernel=(double **) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
cristy3ed852e2009-09-05 21:47:34 +0000249 if (kernel == (double **) NULL)
250 {
251 edge_image=DestroyImage(edge_image);
252 blur_image=DestroyImage(blur_image);
253 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
254 }
255 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000256 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000257 {
cristyf5d0f3e2012-01-17 03:20:33 +0000258 kernel[i]=(double *) AcquireAlignedMemory((size_t) (width-i),(width-i)*
cristy3ed852e2009-09-05 21:47:34 +0000259 sizeof(**kernel));
260 if (kernel[i] == (double *) NULL)
261 break;
cristy47e00502009-12-17 19:19:57 +0000262 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000263 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000264 k=0;
265 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000266 {
cristy47e00502009-12-17 19:19:57 +0000267 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000268 {
cristy4205a3c2010-09-12 20:19:59 +0000269 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
270 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000271 normalize+=kernel[i][k];
272 k++;
cristy3ed852e2009-09-05 21:47:34 +0000273 }
274 }
cristy3ed852e2009-09-05 21:47:34 +0000275 if (fabs(normalize) <= MagickEpsilon)
276 normalize=1.0;
277 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000278 for (k=0; k < (j*j); k++)
279 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000280 }
cristybb503372010-05-27 20:51:26 +0000281 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000282 {
283 for (i-=2; i >= 0; i-=2)
cristyf5d0f3e2012-01-17 03:20:33 +0000284 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
cristy219a63a2012-01-17 12:30:33 +0000285 kernel=(double **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000286 edge_image=DestroyImage(edge_image);
287 blur_image=DestroyImage(blur_image);
288 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
289 }
290 /*
291 Adaptively blur image.
292 */
293 status=MagickTrue;
294 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000295 image_view=AcquireCacheView(image);
296 edge_view=AcquireCacheView(edge_image);
297 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000298#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000299 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000300#endif
cristybb503372010-05-27 20:51:26 +0000301 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000302 {
cristy4c08aed2011-07-01 19:47:50 +0000303 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000304 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000305
cristy4c08aed2011-07-01 19:47:50 +0000306 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000307 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000308
cristy117ff172010-08-15 21:35:32 +0000309 register ssize_t
310 x;
311
cristy3ed852e2009-09-05 21:47:34 +0000312 if (status == MagickFalse)
313 continue;
314 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
315 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
316 exception);
cristyacd2ed22011-08-30 01:44:23 +0000317 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000318 {
319 status=MagickFalse;
320 continue;
321 }
cristybb503372010-05-27 20:51:26 +0000322 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000323 {
cristy4c11c2b2011-09-05 20:17:07 +0000324 register const Quantum
325 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000326
cristybb503372010-05-27 20:51:26 +0000327 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000328 i;
cristy3ed852e2009-09-05 21:47:34 +0000329
cristy4c11c2b2011-09-05 20:17:07 +0000330 ssize_t
331 center,
332 j;
333
334 j=(ssize_t) ceil((double) width*QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +0000335 GetPixelIntensity(edge_image,r)-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000336 if (j < 0)
337 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000338 else
cristy4c11c2b2011-09-05 20:17:07 +0000339 if (j > (ssize_t) width)
340 j=(ssize_t) width;
341 if ((j & 0x01) != 0)
342 j--;
343 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
344 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000345 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000346 break;
cristyd09f8802012-02-04 16:44:10 +0000347 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
348 GetPixelChannels(image)*((width-j)/2L);
cristy4c11c2b2011-09-05 20:17:07 +0000349 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000350 {
cristy4c11c2b2011-09-05 20:17:07 +0000351 MagickRealType
352 alpha,
353 gamma,
354 pixel;
355
356 PixelChannel
357 channel;
358
359 PixelTrait
360 blur_traits,
361 traits;
362
363 register const double
364 *restrict k;
365
366 register const Quantum
367 *restrict pixels;
368
369 register ssize_t
370 u;
371
372 ssize_t
373 v;
374
cristye2a912b2011-12-05 20:02:07 +0000375 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000376 traits=GetPixelChannelMapTraits(image,channel);
cristy4c11c2b2011-09-05 20:17:07 +0000377 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
378 if ((traits == UndefinedPixelTrait) ||
379 (blur_traits == UndefinedPixelTrait))
380 continue;
cristyd09f8802012-02-04 16:44:10 +0000381 if (((blur_traits & CopyPixelTrait) != 0) ||
382 (GetPixelMask(image,q) != 0))
cristy4c11c2b2011-09-05 20:17:07 +0000383 {
cristy0beccfa2011-09-25 20:47:53 +0000384 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy4c11c2b2011-09-05 20:17:07 +0000385 continue;
386 }
387 k=kernel[j];
388 pixels=p;
389 pixel=bias;
390 gamma=0.0;
391 if ((blur_traits & BlendPixelTrait) == 0)
392 {
393 /*
394 No alpha blending.
395 */
396 for (v=0; v < (ssize_t) (width-j); v++)
397 {
398 for (u=0; u < (ssize_t) (width-j); u++)
399 {
400 pixel+=(*k)*pixels[i];
401 gamma+=(*k);
402 k++;
403 pixels+=GetPixelChannels(image);
404 }
405 }
406 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +0000407 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy4c11c2b2011-09-05 20:17:07 +0000408 continue;
409 }
410 /*
411 Alpha blending.
412 */
413 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000414 {
cristy4c11c2b2011-09-05 20:17:07 +0000415 for (u=0; u < (ssize_t) (width-j); u++)
416 {
417 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
418 pixel+=(*k)*alpha*pixels[i];
419 gamma+=(*k)*alpha;
420 k++;
421 pixels+=GetPixelChannels(image);
422 }
cristy3ed852e2009-09-05 21:47:34 +0000423 }
cristy4c11c2b2011-09-05 20:17:07 +0000424 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +0000425 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy3ed852e2009-09-05 21:47:34 +0000426 }
cristyed231572011-07-14 02:18:59 +0000427 q+=GetPixelChannels(blur_image);
428 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000429 }
430 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
431 status=MagickFalse;
432 if (image->progress_monitor != (MagickProgressMonitor) NULL)
433 {
434 MagickBooleanType
435 proceed;
436
cristyb5d5f722009-11-04 03:03:49 +0000437#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +0000438 #pragma omp critical (MagickCore_AdaptiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +0000439#endif
440 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
441 image->rows);
442 if (proceed == MagickFalse)
443 status=MagickFalse;
444 }
445 }
446 blur_image->type=image->type;
447 blur_view=DestroyCacheView(blur_view);
448 edge_view=DestroyCacheView(edge_view);
449 image_view=DestroyCacheView(image_view);
450 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000451 for (i=0; i < (ssize_t) width; i+=2)
cristyf5d0f3e2012-01-17 03:20:33 +0000452 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
cristy219a63a2012-01-17 12:30:33 +0000453 kernel=(double **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000454 if (status == MagickFalse)
455 blur_image=DestroyImage(blur_image);
456 return(blur_image);
457}
458
459/*
460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461% %
462% %
463% %
464% A d a p t i v e S h a r p e n I m a g e %
465% %
466% %
467% %
468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469%
470% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
471% intensely near image edges and less intensely far from edges. We sharpen the
472% image with a Gaussian operator of the given radius and standard deviation
473% (sigma). For reasonable results, radius should be larger than sigma. Use a
474% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
475%
476% The format of the AdaptiveSharpenImage method is:
477%
478% Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000479% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000480%
481% A description of each parameter follows:
482%
483% o image: the image.
484%
cristy3ed852e2009-09-05 21:47:34 +0000485% o radius: the radius of the Gaussian, in pixels, not counting the center
486% pixel.
487%
488% o sigma: the standard deviation of the Laplacian, in pixels.
489%
cristy4c11c2b2011-09-05 20:17:07 +0000490% o bias: the bias.
491%
cristy3ed852e2009-09-05 21:47:34 +0000492% o exception: return any errors or warnings in this structure.
493%
494*/
cristy3ed852e2009-09-05 21:47:34 +0000495MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristy4c11c2b2011-09-05 20:17:07 +0000496 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000497{
cristy3ed852e2009-09-05 21:47:34 +0000498#define AdaptiveSharpenImageTag "Convolve/Image"
499#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
500
cristyc4c8d132010-01-07 01:58:38 +0000501 CacheView
502 *sharp_view,
503 *edge_view,
504 *image_view;
505
cristy3ed852e2009-09-05 21:47:34 +0000506 double
cristy47e00502009-12-17 19:19:57 +0000507 **kernel,
508 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000509
510 Image
511 *sharp_image,
512 *edge_image,
513 *gaussian_image;
514
cristy3ed852e2009-09-05 21:47:34 +0000515 MagickBooleanType
516 status;
517
cristybb503372010-05-27 20:51:26 +0000518 MagickOffsetType
519 progress;
520
cristybb503372010-05-27 20:51:26 +0000521 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000522 i;
cristy3ed852e2009-09-05 21:47:34 +0000523
cristybb503372010-05-27 20:51:26 +0000524 size_t
cristy3ed852e2009-09-05 21:47:34 +0000525 width;
526
cristybb503372010-05-27 20:51:26 +0000527 ssize_t
528 j,
529 k,
530 u,
531 v,
532 y;
533
cristy3ed852e2009-09-05 21:47:34 +0000534 assert(image != (const Image *) NULL);
535 assert(image->signature == MagickSignature);
536 if (image->debug != MagickFalse)
537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
538 assert(exception != (ExceptionInfo *) NULL);
539 assert(exception->signature == MagickSignature);
540 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
541 if (sharp_image == (Image *) NULL)
542 return((Image *) NULL);
543 if (fabs(sigma) <= MagickEpsilon)
544 return(sharp_image);
cristy574cc262011-08-05 01:23:58 +0000545 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000546 {
cristy3ed852e2009-09-05 21:47:34 +0000547 sharp_image=DestroyImage(sharp_image);
548 return((Image *) NULL);
549 }
550 /*
551 Edge detect the image brighness channel, level, sharp, and level again.
552 */
cristy8ae632d2011-09-05 17:29:53 +0000553 edge_image=EdgeImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000554 if (edge_image == (Image *) NULL)
555 {
556 sharp_image=DestroyImage(sharp_image);
557 return((Image *) NULL);
558 }
cristy051718b2011-08-28 22:49:25 +0000559 (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
cristyd89705a2012-01-20 02:52:24 +0000560 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000561 if (gaussian_image != (Image *) NULL)
562 {
563 edge_image=DestroyImage(edge_image);
564 edge_image=gaussian_image;
565 }
cristy051718b2011-08-28 22:49:25 +0000566 (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
cristy3ed852e2009-09-05 21:47:34 +0000567 /*
568 Create a set of kernels from maximum (radius,sigma) to minimum.
569 */
570 width=GetOptimalKernelWidth2D(radius,sigma);
cristy219a63a2012-01-17 12:30:33 +0000571 kernel=(double **) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
cristy3ed852e2009-09-05 21:47:34 +0000572 if (kernel == (double **) NULL)
573 {
574 edge_image=DestroyImage(edge_image);
575 sharp_image=DestroyImage(sharp_image);
576 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
577 }
578 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000579 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000580 {
cristy59d1f6d2012-01-17 03:17:40 +0000581 kernel[i]=(double *) AcquireAlignedMemory((size_t) (width-i),(width-i)*
cristy3ed852e2009-09-05 21:47:34 +0000582 sizeof(**kernel));
583 if (kernel[i] == (double *) NULL)
584 break;
cristy47e00502009-12-17 19:19:57 +0000585 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000586 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000587 k=0;
588 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000589 {
cristy47e00502009-12-17 19:19:57 +0000590 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000591 {
cristy4205a3c2010-09-12 20:19:59 +0000592 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
593 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000594 normalize+=kernel[i][k];
595 k++;
cristy3ed852e2009-09-05 21:47:34 +0000596 }
597 }
cristy3ed852e2009-09-05 21:47:34 +0000598 if (fabs(normalize) <= MagickEpsilon)
599 normalize=1.0;
600 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000601 for (k=0; k < (j*j); k++)
602 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000603 }
cristybb503372010-05-27 20:51:26 +0000604 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000605 {
606 for (i-=2; i >= 0; i-=2)
cristy59d1f6d2012-01-17 03:17:40 +0000607 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
cristy219a63a2012-01-17 12:30:33 +0000608 kernel=(double **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000609 edge_image=DestroyImage(edge_image);
610 sharp_image=DestroyImage(sharp_image);
611 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
612 }
613 /*
614 Adaptively sharpen image.
615 */
616 status=MagickTrue;
617 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000618 image_view=AcquireCacheView(image);
619 edge_view=AcquireCacheView(edge_image);
620 sharp_view=AcquireCacheView(sharp_image);
cristyb5d5f722009-11-04 03:03:49 +0000621#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000622 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000623#endif
cristybb503372010-05-27 20:51:26 +0000624 for (y=0; y < (ssize_t) sharp_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000625 {
cristy4c08aed2011-07-01 19:47:50 +0000626 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000627 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000628
cristy4c08aed2011-07-01 19:47:50 +0000629 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000630 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000631
cristy117ff172010-08-15 21:35:32 +0000632 register ssize_t
633 x;
634
cristy3ed852e2009-09-05 21:47:34 +0000635 if (status == MagickFalse)
636 continue;
637 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
638 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
639 exception);
cristy4c08aed2011-07-01 19:47:50 +0000640 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000641 {
642 status=MagickFalse;
643 continue;
644 }
cristybb503372010-05-27 20:51:26 +0000645 for (x=0; x < (ssize_t) sharp_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000646 {
cristy4c11c2b2011-09-05 20:17:07 +0000647 register const Quantum
648 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000649
cristybb503372010-05-27 20:51:26 +0000650 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000651 i;
cristy3ed852e2009-09-05 21:47:34 +0000652
cristy4c11c2b2011-09-05 20:17:07 +0000653 ssize_t
654 center,
655 j;
656
657 j=(ssize_t) ceil((double) width*QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +0000658 GetPixelIntensity(edge_image,r)-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000659 if (j < 0)
660 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000661 else
cristy4c11c2b2011-09-05 20:17:07 +0000662 if (j > (ssize_t) width)
663 j=(ssize_t) width;
664 if ((j & 0x01) != 0)
665 j--;
666 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
667 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000668 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000669 break;
cristyd09f8802012-02-04 16:44:10 +0000670 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
671 GetPixelChannels(image)*((width-j)/2);
cristyc94ba6f2012-01-29 23:19:58 +0000672 for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000673 {
cristy4c11c2b2011-09-05 20:17:07 +0000674 MagickRealType
675 alpha,
676 gamma,
677 pixel;
678
679 PixelChannel
680 channel;
681
682 PixelTrait
683 sharp_traits,
684 traits;
685
686 register const double
687 *restrict k;
688
689 register const Quantum
690 *restrict pixels;
691
692 register ssize_t
693 u;
694
695 ssize_t
696 v;
697
cristye2a912b2011-12-05 20:02:07 +0000698 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000699 traits=GetPixelChannelMapTraits(image,channel);
cristy4c11c2b2011-09-05 20:17:07 +0000700 sharp_traits=GetPixelChannelMapTraits(sharp_image,channel);
701 if ((traits == UndefinedPixelTrait) ||
702 (sharp_traits == UndefinedPixelTrait))
703 continue;
cristyd09f8802012-02-04 16:44:10 +0000704 if (((sharp_traits & CopyPixelTrait) != 0) ||
705 (GetPixelMask(image,q) != 0))
cristy4c11c2b2011-09-05 20:17:07 +0000706 {
cristy0beccfa2011-09-25 20:47:53 +0000707 SetPixelChannel(sharp_image,channel,p[center+i],q);
cristy4c11c2b2011-09-05 20:17:07 +0000708 continue;
709 }
710 k=kernel[j];
711 pixels=p;
712 pixel=bias;
713 gamma=0.0;
714 if ((sharp_traits & BlendPixelTrait) == 0)
715 {
716 /*
717 No alpha blending.
718 */
719 for (v=0; v < (ssize_t) (width-j); v++)
720 {
721 for (u=0; u < (ssize_t) (width-j); u++)
722 {
723 pixel+=(*k)*pixels[i];
724 gamma+=(*k);
725 k++;
726 pixels+=GetPixelChannels(image);
727 }
728 }
729 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +0000730 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
cristy4c11c2b2011-09-05 20:17:07 +0000731 continue;
732 }
733 /*
734 Alpha blending.
735 */
736 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000737 {
cristy4c11c2b2011-09-05 20:17:07 +0000738 for (u=0; u < (ssize_t) (width-j); u++)
739 {
740 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
741 pixel+=(*k)*alpha*pixels[i];
742 gamma+=(*k)*alpha;
743 k++;
744 pixels+=GetPixelChannels(image);
745 }
cristy3ed852e2009-09-05 21:47:34 +0000746 }
cristy4c11c2b2011-09-05 20:17:07 +0000747 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +0000748 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
cristy3ed852e2009-09-05 21:47:34 +0000749 }
cristyed231572011-07-14 02:18:59 +0000750 q+=GetPixelChannels(sharp_image);
751 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000752 }
753 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
754 status=MagickFalse;
755 if (image->progress_monitor != (MagickProgressMonitor) NULL)
756 {
757 MagickBooleanType
758 proceed;
759
cristyb5d5f722009-11-04 03:03:49 +0000760#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +0000761 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000762#endif
763 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
764 image->rows);
765 if (proceed == MagickFalse)
766 status=MagickFalse;
767 }
768 }
769 sharp_image->type=image->type;
770 sharp_view=DestroyCacheView(sharp_view);
771 edge_view=DestroyCacheView(edge_view);
772 image_view=DestroyCacheView(image_view);
773 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000774 for (i=0; i < (ssize_t) width; i+=2)
cristy59d1f6d2012-01-17 03:17:40 +0000775 kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
cristy219a63a2012-01-17 12:30:33 +0000776 kernel=(double **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000777 if (status == MagickFalse)
778 sharp_image=DestroyImage(sharp_image);
779 return(sharp_image);
780}
781
782/*
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784% %
785% %
786% %
787% B l u r I m a g e %
788% %
789% %
790% %
791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
792%
793% BlurImage() blurs an image. We convolve the image with a Gaussian operator
794% of the given radius and standard deviation (sigma). For reasonable results,
795% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
796% selects a suitable radius for you.
797%
798% BlurImage() differs from GaussianBlurImage() in that it uses a separable
799% kernel which is faster but mathematically equivalent to the non-separable
800% kernel.
801%
802% The format of the BlurImage method is:
803%
804% Image *BlurImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000805% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000806%
807% A description of each parameter follows:
808%
809% o image: the image.
810%
cristy3ed852e2009-09-05 21:47:34 +0000811% o radius: the radius of the Gaussian, in pixels, not counting the center
812% pixel.
813%
814% o sigma: the standard deviation of the Gaussian, in pixels.
815%
cristy05c0c9a2011-09-05 23:16:13 +0000816% o bias: the bias.
817%
cristy3ed852e2009-09-05 21:47:34 +0000818% o exception: return any errors or warnings in this structure.
819%
820*/
821
cristybb503372010-05-27 20:51:26 +0000822static double *GetBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000823{
cristy3ed852e2009-09-05 21:47:34 +0000824 double
cristy47e00502009-12-17 19:19:57 +0000825 *kernel,
826 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000827
cristy117ff172010-08-15 21:35:32 +0000828 register ssize_t
829 i;
830
cristybb503372010-05-27 20:51:26 +0000831 ssize_t
cristy47e00502009-12-17 19:19:57 +0000832 j,
833 k;
cristy3ed852e2009-09-05 21:47:34 +0000834
cristy3ed852e2009-09-05 21:47:34 +0000835 /*
836 Generate a 1-D convolution kernel.
837 */
838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy8b49f382012-01-17 03:11:03 +0000839 kernel=(double *) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
cristy3ed852e2009-09-05 21:47:34 +0000840 if (kernel == (double *) NULL)
841 return(0);
cristy3ed852e2009-09-05 21:47:34 +0000842 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000843 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000844 i=0;
845 for (k=(-j); k <= j; k++)
846 {
cristy4205a3c2010-09-12 20:19:59 +0000847 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
848 (MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +0000849 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +0000850 i++;
851 }
cristybb503372010-05-27 20:51:26 +0000852 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000853 kernel[i]/=normalize;
854 return(kernel);
855}
856
cristyf4ad9df2011-07-08 16:49:03 +0000857MagickExport Image *BlurImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +0000858 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000859{
860#define BlurImageTag "Blur/Image"
861
cristyc4c8d132010-01-07 01:58:38 +0000862 CacheView
863 *blur_view,
864 *image_view;
865
cristy3ed852e2009-09-05 21:47:34 +0000866 double
867 *kernel;
868
869 Image
870 *blur_image;
871
cristy3ed852e2009-09-05 21:47:34 +0000872 MagickBooleanType
873 status;
874
cristybb503372010-05-27 20:51:26 +0000875 MagickOffsetType
876 progress;
877
cristybb503372010-05-27 20:51:26 +0000878 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000879 i;
880
cristybb503372010-05-27 20:51:26 +0000881 size_t
cristy3ed852e2009-09-05 21:47:34 +0000882 width;
883
cristybb503372010-05-27 20:51:26 +0000884 ssize_t
cristyb41a1172011-09-06 00:55:14 +0000885 center,
cristybb503372010-05-27 20:51:26 +0000886 x,
887 y;
888
cristy3ed852e2009-09-05 21:47:34 +0000889 /*
890 Initialize blur image attributes.
891 */
892 assert(image != (Image *) NULL);
893 assert(image->signature == MagickSignature);
894 if (image->debug != MagickFalse)
895 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
896 assert(exception != (ExceptionInfo *) NULL);
897 assert(exception->signature == MagickSignature);
cristyd25c77e2011-09-06 00:10:24 +0000898 blur_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000899 if (blur_image == (Image *) NULL)
900 return((Image *) NULL);
901 if (fabs(sigma) <= MagickEpsilon)
902 return(blur_image);
cristy574cc262011-08-05 01:23:58 +0000903 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000904 {
cristy3ed852e2009-09-05 21:47:34 +0000905 blur_image=DestroyImage(blur_image);
906 return((Image *) NULL);
907 }
908 width=GetOptimalKernelWidth1D(radius,sigma);
909 kernel=GetBlurKernel(width,sigma);
910 if (kernel == (double *) NULL)
911 {
912 blur_image=DestroyImage(blur_image);
913 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
914 }
915 if (image->debug != MagickFalse)
916 {
917 char
918 format[MaxTextExtent],
919 *message;
920
921 register const double
922 *k;
923
924 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristyef1f9072012-01-20 14:43:06 +0000925 " blur image with kernel width %.20g:",(double) width);
cristy3ed852e2009-09-05 21:47:34 +0000926 message=AcquireString("");
927 k=kernel;
cristybb503372010-05-27 20:51:26 +0000928 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000929 {
930 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000931 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
cristy3ed852e2009-09-05 21:47:34 +0000932 (void) ConcatenateString(&message,format);
cristyb51dff52011-05-19 16:55:47 +0000933 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristy3ed852e2009-09-05 21:47:34 +0000934 (void) ConcatenateString(&message,format);
935 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
936 }
937 message=DestroyString(message);
938 }
939 /*
940 Blur rows.
941 */
942 status=MagickTrue;
943 progress=0;
cristyb41a1172011-09-06 00:55:14 +0000944 center=(ssize_t) GetPixelChannels(image)*(width/2L);
cristy3ed852e2009-09-05 21:47:34 +0000945 image_view=AcquireCacheView(image);
946 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000947#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000948 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000949#endif
cristyb41a1172011-09-06 00:55:14 +0000950 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000951 {
cristy4c08aed2011-07-01 19:47:50 +0000952 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000953 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000954
cristy4c08aed2011-07-01 19:47:50 +0000955 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000956 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000957
cristy117ff172010-08-15 21:35:32 +0000958 register ssize_t
959 x;
960
cristy3ed852e2009-09-05 21:47:34 +0000961 if (status == MagickFalse)
962 continue;
cristy117ff172010-08-15 21:35:32 +0000963 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
964 image->columns+width,1,exception);
cristy8b49f382012-01-17 03:11:03 +0000965 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000966 exception);
cristy4c08aed2011-07-01 19:47:50 +0000967 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000968 {
969 status=MagickFalse;
970 continue;
971 }
cristyb41a1172011-09-06 00:55:14 +0000972 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000973 {
cristybb503372010-05-27 20:51:26 +0000974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000975 i;
976
cristyb41a1172011-09-06 00:55:14 +0000977 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
978 {
979 MagickRealType
980 alpha,
981 gamma,
982 pixel;
cristyd25c77e2011-09-06 00:10:24 +0000983
cristyb41a1172011-09-06 00:55:14 +0000984 PixelChannel
985 channel;
986
987 PixelTrait
988 blur_traits,
989 traits;
990
991 register const double
992 *restrict k;
993
994 register const Quantum
995 *restrict pixels;
996
997 register ssize_t
998 u;
999
cristye2a912b2011-12-05 20:02:07 +00001000 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00001001 traits=GetPixelChannelMapTraits(image,channel);
cristyb41a1172011-09-06 00:55:14 +00001002 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1003 if ((traits == UndefinedPixelTrait) ||
1004 (blur_traits == UndefinedPixelTrait))
1005 continue;
cristyd09f8802012-02-04 16:44:10 +00001006 if (((blur_traits & CopyPixelTrait) != 0) ||
1007 (GetPixelMask(image,p) != 0))
cristyd25c77e2011-09-06 00:10:24 +00001008 {
cristy0beccfa2011-09-25 20:47:53 +00001009 SetPixelChannel(blur_image,channel,p[center+i],q);
cristyb41a1172011-09-06 00:55:14 +00001010 continue;
cristyd25c77e2011-09-06 00:10:24 +00001011 }
cristyb41a1172011-09-06 00:55:14 +00001012 k=kernel;
1013 pixels=p;
1014 pixel=0.0;
1015 if ((blur_traits & BlendPixelTrait) == 0)
1016 {
1017 /*
1018 No alpha blending.
1019 */
1020 for (u=0; u < (ssize_t) width; u++)
cristyd25c77e2011-09-06 00:10:24 +00001021 {
cristyb41a1172011-09-06 00:55:14 +00001022 pixel+=(*k)*pixels[i];
1023 k++;
1024 pixels+=GetPixelChannels(image);
cristyd25c77e2011-09-06 00:10:24 +00001025 }
cristy0beccfa2011-09-25 20:47:53 +00001026 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
cristyb41a1172011-09-06 00:55:14 +00001027 continue;
1028 }
1029 /*
1030 Alpha blending.
1031 */
1032 gamma=0.0;
1033 for (u=0; u < (ssize_t) width; u++)
1034 {
1035 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1036 pixel+=(*k)*alpha*pixels[i];
1037 gamma+=(*k)*alpha;
1038 k++;
1039 pixels+=GetPixelChannels(image);
cristyd25c77e2011-09-06 00:10:24 +00001040 }
cristyb41a1172011-09-06 00:55:14 +00001041 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00001042 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristyb41a1172011-09-06 00:55:14 +00001043 }
cristyed231572011-07-14 02:18:59 +00001044 p+=GetPixelChannels(image);
1045 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001046 }
1047 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1048 status=MagickFalse;
1049 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1050 {
1051 MagickBooleanType
1052 proceed;
1053
cristyb5d5f722009-11-04 03:03:49 +00001054#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyef1f9072012-01-20 14:43:06 +00001055 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001056#endif
1057 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1058 blur_image->columns);
1059 if (proceed == MagickFalse)
1060 status=MagickFalse;
1061 }
1062 }
1063 blur_view=DestroyCacheView(blur_view);
1064 image_view=DestroyCacheView(image_view);
1065 /*
1066 Blur columns.
1067 */
cristyef1f9072012-01-20 14:43:06 +00001068 center=(ssize_t) GetPixelChannels(blur_image)*(width/2L);
cristy3ed852e2009-09-05 21:47:34 +00001069 image_view=AcquireCacheView(blur_image);
1070 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00001071#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001072 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001073#endif
cristyb6dc4b72011-12-10 23:29:53 +00001074 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001075 {
cristy4c08aed2011-07-01 19:47:50 +00001076 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001077 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001078
cristy4c08aed2011-07-01 19:47:50 +00001079 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001080 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001081
cristy117ff172010-08-15 21:35:32 +00001082 register ssize_t
1083 y;
1084
cristy3ed852e2009-09-05 21:47:34 +00001085 if (status == MagickFalse)
1086 continue;
cristy117ff172010-08-15 21:35:32 +00001087 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
cristyb6dc4b72011-12-10 23:29:53 +00001088 blur_image->rows+width,exception);
cristy3ed852e2009-09-05 21:47:34 +00001089 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00001090 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001091 {
1092 status=MagickFalse;
1093 continue;
1094 }
cristyb6dc4b72011-12-10 23:29:53 +00001095 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001096 {
cristybb503372010-05-27 20:51:26 +00001097 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001098 i;
1099
cristyb6dc4b72011-12-10 23:29:53 +00001100 for (i=0; i < (ssize_t) GetPixelChannels(blur_image); i++)
cristyb41a1172011-09-06 00:55:14 +00001101 {
1102 MagickRealType
1103 alpha,
1104 gamma,
1105 pixel;
cristyd25c77e2011-09-06 00:10:24 +00001106
cristyb41a1172011-09-06 00:55:14 +00001107 PixelChannel
1108 channel;
1109
1110 PixelTrait
1111 blur_traits,
1112 traits;
1113
1114 register const double
1115 *restrict k;
1116
1117 register const Quantum
1118 *restrict pixels;
1119
1120 register ssize_t
1121 u;
1122
cristye2a912b2011-12-05 20:02:07 +00001123 channel=GetPixelChannelMapChannel(blur_image,i);
cristyabace412011-12-11 15:56:53 +00001124 traits=GetPixelChannelMapTraits(blur_image,channel);
cristyb41a1172011-09-06 00:55:14 +00001125 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1126 if ((traits == UndefinedPixelTrait) ||
1127 (blur_traits == UndefinedPixelTrait))
1128 continue;
cristyd09f8802012-02-04 16:44:10 +00001129 if (((blur_traits & CopyPixelTrait) != 0) ||
1130 (GetPixelMask(blur_image,p) != 0))
cristyd25c77e2011-09-06 00:10:24 +00001131 {
cristy0beccfa2011-09-25 20:47:53 +00001132 SetPixelChannel(blur_image,channel,p[center+i],q);
cristyb41a1172011-09-06 00:55:14 +00001133 continue;
cristyd25c77e2011-09-06 00:10:24 +00001134 }
cristyb41a1172011-09-06 00:55:14 +00001135 k=kernel;
1136 pixels=p;
1137 pixel=0.0;
1138 if ((blur_traits & BlendPixelTrait) == 0)
1139 {
1140 /*
1141 No alpha blending.
1142 */
1143 for (u=0; u < (ssize_t) width; u++)
cristyd25c77e2011-09-06 00:10:24 +00001144 {
cristyb41a1172011-09-06 00:55:14 +00001145 pixel+=(*k)*pixels[i];
1146 k++;
1147 pixels+=GetPixelChannels(blur_image);
cristyd25c77e2011-09-06 00:10:24 +00001148 }
cristy0beccfa2011-09-25 20:47:53 +00001149 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
cristyb41a1172011-09-06 00:55:14 +00001150 continue;
1151 }
1152 /*
1153 Alpha blending.
1154 */
1155 gamma=0.0;
1156 for (u=0; u < (ssize_t) width; u++)
1157 {
cristyb6dc4b72011-12-10 23:29:53 +00001158 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(blur_image,
1159 pixels));
cristyb41a1172011-09-06 00:55:14 +00001160 pixel+=(*k)*alpha*pixels[i];
1161 gamma+=(*k)*alpha;
1162 k++;
1163 pixels+=GetPixelChannels(blur_image);
cristyd25c77e2011-09-06 00:10:24 +00001164 }
cristyb41a1172011-09-06 00:55:14 +00001165 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00001166 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristyb41a1172011-09-06 00:55:14 +00001167 }
cristyd25c77e2011-09-06 00:10:24 +00001168 p+=GetPixelChannels(blur_image);
cristyed231572011-07-14 02:18:59 +00001169 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001170 }
1171 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1172 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00001173 if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001174 {
1175 MagickBooleanType
1176 proceed;
1177
cristyb5d5f722009-11-04 03:03:49 +00001178#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyef1f9072012-01-20 14:43:06 +00001179 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001180#endif
cristy4c08aed2011-07-01 19:47:50 +00001181 proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1182 blur_image->rows+blur_image->columns);
cristy3ed852e2009-09-05 21:47:34 +00001183 if (proceed == MagickFalse)
1184 status=MagickFalse;
1185 }
1186 }
1187 blur_view=DestroyCacheView(blur_view);
1188 image_view=DestroyCacheView(image_view);
cristy8b49f382012-01-17 03:11:03 +00001189 kernel=(double *) RelinquishAlignedMemory(kernel);
1190 blur_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +00001191 if (status == MagickFalse)
1192 blur_image=DestroyImage(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001193 return(blur_image);
1194}
1195
1196/*
1197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1198% %
1199% %
1200% %
cristyfccdab92009-11-30 16:43:57 +00001201% C o n v o l v e I m a g e %
1202% %
1203% %
1204% %
1205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1206%
1207% ConvolveImage() applies a custom convolution kernel to the image.
1208%
1209% The format of the ConvolveImage method is:
1210%
cristy5e6be1e2011-07-16 01:23:39 +00001211% Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1212% ExceptionInfo *exception)
1213%
cristyfccdab92009-11-30 16:43:57 +00001214% A description of each parameter follows:
1215%
1216% o image: the image.
1217%
cristy5e6be1e2011-07-16 01:23:39 +00001218% o kernel: the filtering kernel.
cristyfccdab92009-11-30 16:43:57 +00001219%
1220% o exception: return any errors or warnings in this structure.
1221%
1222*/
cristy5e6be1e2011-07-16 01:23:39 +00001223MagickExport Image *ConvolveImage(const Image *image,
1224 const KernelInfo *kernel_info,ExceptionInfo *exception)
cristyfccdab92009-11-30 16:43:57 +00001225{
cristy1d326872012-03-07 22:21:31 +00001226 return(MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception));
cristyfccdab92009-11-30 16:43:57 +00001227}
1228
1229/*
1230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231% %
1232% %
1233% %
cristy3ed852e2009-09-05 21:47:34 +00001234% D e s p e c k l e I m a g e %
1235% %
1236% %
1237% %
1238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239%
1240% DespeckleImage() reduces the speckle noise in an image while perserving the
cristyf0ae7762012-01-15 22:04:03 +00001241% edges of the original image. A speckle removing filter uses a complementary % hulling technique (raising pixels that are darker than their surrounding
1242% neighbors, then complementarily lowering pixels that are brighter than their
1243% surrounding neighbors) to reduce the speckle index of that image (reference
1244% Crimmins speckle removal).
cristy3ed852e2009-09-05 21:47:34 +00001245%
1246% The format of the DespeckleImage method is:
1247%
1248% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1249%
1250% A description of each parameter follows:
1251%
1252% o image: the image.
1253%
1254% o exception: return any errors or warnings in this structure.
1255%
1256*/
1257
cristy5473bf02012-01-16 19:15:08 +00001258static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1259 const size_t columns,const size_t rows,const int polarity,Quantum *restrict f,
1260 Quantum *restrict g)
cristy3ed852e2009-09-05 21:47:34 +00001261{
cristy7b4b8862012-01-16 19:52:19 +00001262 register Quantum
cristy5473bf02012-01-16 19:15:08 +00001263 *p,
1264 *q,
1265 *r,
1266 *s;
cristy78f3de22012-01-16 00:43:18 +00001267
cristy5473bf02012-01-16 19:15:08 +00001268 ssize_t
1269 y;
1270
1271 assert(f != (Quantum *) NULL);
1272 assert(g != (Quantum *) NULL);
1273 p=f+(columns+2);
1274 q=g+(columns+2);
1275 r=p+(y_offset*(columns+2)+x_offset);
cristyff8e85a2012-01-18 13:36:20 +00001276#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy8de86be2012-01-20 02:05:06 +00001277 #pragma omp parallel for schedule(static)
cristy5473bf02012-01-16 19:15:08 +00001278#endif
1279 for (y=0; y < (ssize_t) rows; y++)
1280 {
cristy7b4b8862012-01-16 19:52:19 +00001281 register ssize_t
cristy5473bf02012-01-16 19:15:08 +00001282 i,
1283 x;
1284
cristy7b4b8862012-01-16 19:52:19 +00001285 SignedQuantum
1286 v;
1287
cristy5473bf02012-01-16 19:15:08 +00001288 i=(2*y+1)+y*columns;
1289 if (polarity > 0)
1290 for (x=0; x < (ssize_t) columns; x++)
1291 {
1292 v=(SignedQuantum) p[i];
1293 if ((SignedQuantum) r[i] >= (v+ScaleCharToQuantum(2)))
1294 v+=ScaleCharToQuantum(1);
1295 q[i]=(Quantum) v;
1296 i++;
1297 }
1298 else
1299 for (x=0; x < (ssize_t) columns; x++)
1300 {
1301 v=(SignedQuantum) p[i];
1302 if ((SignedQuantum) r[i] <= (v-ScaleCharToQuantum(2)))
1303 v-=ScaleCharToQuantum(1);
1304 q[i]=(Quantum) v;
1305 i++;
1306 }
1307 }
1308 p=f+(columns+2);
1309 q=g+(columns+2);
1310 r=q+(y_offset*(columns+2)+x_offset);
1311 s=q-(y_offset*(columns+2)+x_offset);
cristyff8e85a2012-01-18 13:36:20 +00001312#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy8de86be2012-01-20 02:05:06 +00001313 #pragma omp parallel for schedule(static)
cristy5473bf02012-01-16 19:15:08 +00001314#endif
1315 for (y=0; y < (ssize_t) rows; y++)
1316 {
cristy7b4b8862012-01-16 19:52:19 +00001317 register ssize_t
cristy5473bf02012-01-16 19:15:08 +00001318 i,
1319 x;
1320
cristy7b4b8862012-01-16 19:52:19 +00001321 SignedQuantum
1322 v;
1323
cristy5473bf02012-01-16 19:15:08 +00001324 i=(2*y+1)+y*columns;
1325 if (polarity > 0)
1326 for (x=0; x < (ssize_t) columns; x++)
1327 {
1328 v=(SignedQuantum) q[i];
1329 if (((SignedQuantum) s[i] >= (v+ScaleCharToQuantum(2))) &&
1330 ((SignedQuantum) r[i] > v))
1331 v+=ScaleCharToQuantum(1);
1332 p[i]=(Quantum) v;
1333 i++;
1334 }
1335 else
1336 for (x=0; x < (ssize_t) columns; x++)
1337 {
1338 v=(SignedQuantum) q[i];
1339 if (((SignedQuantum) s[i] <= (v-ScaleCharToQuantum(2))) &&
1340 ((SignedQuantum) r[i] < v))
1341 v-=ScaleCharToQuantum(1);
1342 p[i]=(Quantum) v;
1343 i++;
1344 }
1345 }
cristy3ed852e2009-09-05 21:47:34 +00001346}
1347
1348MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1349{
1350#define DespeckleImageTag "Despeckle/Image"
1351
cristy2407fc22009-09-11 00:55:25 +00001352 CacheView
1353 *despeckle_view,
1354 *image_view;
1355
cristy3ed852e2009-09-05 21:47:34 +00001356 Image
1357 *despeckle_image;
1358
cristy3ed852e2009-09-05 21:47:34 +00001359 MagickBooleanType
1360 status;
1361
cristy5473bf02012-01-16 19:15:08 +00001362 Quantum
1363 *restrict buffer,
1364 *restrict pixels;
cristya63e4a92011-09-09 00:47:59 +00001365
cristy5473bf02012-01-16 19:15:08 +00001366 register ssize_t
1367 i;
1368
1369 size_t
1370 length;
cristy117ff172010-08-15 21:35:32 +00001371
cristybb503372010-05-27 20:51:26 +00001372 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001373 X[4] = {0, 1, 1,-1},
1374 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001375
cristy3ed852e2009-09-05 21:47:34 +00001376 /*
1377 Allocate despeckled image.
1378 */
1379 assert(image != (const Image *) NULL);
1380 assert(image->signature == MagickSignature);
1381 if (image->debug != MagickFalse)
1382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1383 assert(exception != (ExceptionInfo *) NULL);
1384 assert(exception->signature == MagickSignature);
cristy5473bf02012-01-16 19:15:08 +00001385 despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001386 if (despeckle_image == (Image *) NULL)
1387 return((Image *) NULL);
cristya63e4a92011-09-09 00:47:59 +00001388 status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1389 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001390 {
cristy3ed852e2009-09-05 21:47:34 +00001391 despeckle_image=DestroyImage(despeckle_image);
1392 return((Image *) NULL);
1393 }
1394 /*
cristy5473bf02012-01-16 19:15:08 +00001395 Allocate image buffer.
1396 */
1397 length=(size_t) ((image->columns+2)*(image->rows+2));
1398 pixels=(Quantum *) AcquireQuantumMemory(length,sizeof(*pixels));
1399 buffer=(Quantum *) AcquireQuantumMemory(length,sizeof(*buffer));
1400 if ((pixels == (Quantum *) NULL) || (buffer == (Quantum *) NULL))
1401 {
1402 if (buffer != (Quantum *) NULL)
1403 buffer=(Quantum *) RelinquishMagickMemory(buffer);
1404 if (pixels != (Quantum *) NULL)
1405 pixels=(Quantum *) RelinquishMagickMemory(pixels);
1406 despeckle_image=DestroyImage(despeckle_image);
1407 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1408 }
1409 /*
1410 Reduce speckle in the image.
cristy3ed852e2009-09-05 21:47:34 +00001411 */
1412 status=MagickTrue;
cristyb06da092012-01-16 17:11:38 +00001413 image_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00001414 despeckle_view=AcquireCacheView(despeckle_image);
cristy5473bf02012-01-16 19:15:08 +00001415 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001416 {
cristy5473bf02012-01-16 19:15:08 +00001417 PixelChannel
1418 channel;
1419
1420 PixelTrait
1421 despeckle_traits,
1422 traits;
cristy3ed852e2009-09-05 21:47:34 +00001423
cristyc1488b52011-02-19 18:54:15 +00001424 register ssize_t
cristy5473bf02012-01-16 19:15:08 +00001425 k,
cristyf0ae7762012-01-15 22:04:03 +00001426 x;
cristyc1488b52011-02-19 18:54:15 +00001427
cristy5473bf02012-01-16 19:15:08 +00001428 ssize_t
1429 j,
1430 y;
1431
1432 if (status == MagickFalse)
1433 continue;
1434 channel=GetPixelChannelMapChannel(image,i);
1435 traits=GetPixelChannelMapTraits(image,channel);
1436 despeckle_traits=GetPixelChannelMapTraits(despeckle_image,channel);
1437 if ((traits == UndefinedPixelTrait) ||
1438 (despeckle_traits == UndefinedPixelTrait))
1439 continue;
1440 if ((despeckle_traits & CopyPixelTrait) != 0)
1441 continue;
1442 (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
1443 j=(ssize_t) image->columns+2;
1444 for (y=0; y < (ssize_t) image->rows; y++)
cristyfc830f42012-01-15 02:45:06 +00001445 {
cristy5473bf02012-01-16 19:15:08 +00001446 register const Quantum
1447 *restrict p;
cristyfc830f42012-01-15 02:45:06 +00001448
cristy5473bf02012-01-16 19:15:08 +00001449 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1450 if (p == (const Quantum *) NULL)
cristyf0ae7762012-01-15 22:04:03 +00001451 {
cristy5473bf02012-01-16 19:15:08 +00001452 status=MagickFalse;
1453 continue;
cristyf0ae7762012-01-15 22:04:03 +00001454 }
cristy5473bf02012-01-16 19:15:08 +00001455 j++;
1456 for (x=0; x < (ssize_t) image->columns; x++)
1457 {
1458 pixels[j++]=p[i];
1459 p+=GetPixelChannels(image);
cristyfc830f42012-01-15 02:45:06 +00001460 }
cristy5473bf02012-01-16 19:15:08 +00001461 j++;
cristy3ed852e2009-09-05 21:47:34 +00001462 }
cristy5473bf02012-01-16 19:15:08 +00001463 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1464 for (k=0; k < 4; k++)
1465 {
1466 Hull(X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1467 Hull(-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1468 Hull(-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1469 Hull(X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1470 }
1471 j=(ssize_t) image->columns+2;
1472 for (y=0; y < (ssize_t) image->rows; y++)
1473 {
1474 MagickBooleanType
1475 sync;
1476
1477 register Quantum
1478 *restrict q;
1479
cristyd13e8eb2012-01-17 02:36:42 +00001480 q=QueueCacheViewAuthenticPixels(despeckle_view,0,y,
1481 despeckle_image->columns,1,exception);
cristy5473bf02012-01-16 19:15:08 +00001482 if (q == (Quantum *) NULL)
1483 {
1484 status=MagickFalse;
1485 continue;
1486 }
1487 j++;
1488 for (x=0; x < (ssize_t) image->columns; x++)
1489 {
1490 SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1491 q+=GetPixelChannels(despeckle_image);
1492 }
1493 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1494 if (sync == MagickFalse)
1495 status=MagickFalse;
1496 j++;
1497 }
cristy3ed852e2009-09-05 21:47:34 +00001498 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1499 {
1500 MagickBooleanType
1501 proceed;
1502
cristy5473bf02012-01-16 19:15:08 +00001503 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1504 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00001505 if (proceed == MagickFalse)
1506 status=MagickFalse;
1507 }
1508 }
1509 despeckle_view=DestroyCacheView(despeckle_view);
1510 image_view=DestroyCacheView(image_view);
cristy5473bf02012-01-16 19:15:08 +00001511 buffer=(Quantum *) RelinquishMagickMemory(buffer);
1512 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001513 despeckle_image->type=image->type;
1514 if (status == MagickFalse)
1515 despeckle_image=DestroyImage(despeckle_image);
1516 return(despeckle_image);
1517}
1518
1519/*
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521% %
1522% %
1523% %
1524% E d g e I m a g e %
1525% %
1526% %
1527% %
1528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529%
1530% EdgeImage() finds edges in an image. Radius defines the radius of the
1531% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1532% radius for you.
1533%
1534% The format of the EdgeImage method is:
1535%
1536% Image *EdgeImage(const Image *image,const double radius,
cristy8ae632d2011-09-05 17:29:53 +00001537% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001538%
1539% A description of each parameter follows:
1540%
1541% o image: the image.
1542%
1543% o radius: the radius of the pixel neighborhood.
1544%
cristy8ae632d2011-09-05 17:29:53 +00001545% o sigma: the standard deviation of the Gaussian, in pixels.
1546%
cristy3ed852e2009-09-05 21:47:34 +00001547% o exception: return any errors or warnings in this structure.
1548%
1549*/
1550MagickExport Image *EdgeImage(const Image *image,const double radius,
cristy8ae632d2011-09-05 17:29:53 +00001551 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001552{
1553 Image
1554 *edge_image;
1555
cristy41cbe682011-07-15 19:12:37 +00001556 KernelInfo
1557 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001558
cristybb503372010-05-27 20:51:26 +00001559 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001560 i;
1561
cristybb503372010-05-27 20:51:26 +00001562 size_t
cristy3ed852e2009-09-05 21:47:34 +00001563 width;
1564
cristy41cbe682011-07-15 19:12:37 +00001565 ssize_t
1566 j,
1567 u,
1568 v;
1569
cristy3ed852e2009-09-05 21:47:34 +00001570 assert(image != (const Image *) NULL);
1571 assert(image->signature == MagickSignature);
1572 if (image->debug != MagickFalse)
1573 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1574 assert(exception != (ExceptionInfo *) NULL);
1575 assert(exception->signature == MagickSignature);
cristyfc0ae532011-12-06 15:14:45 +00001576 width=GetOptimalKernelWidth1D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001577 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001578 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001579 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001580 kernel_info->width=width;
1581 kernel_info->height=width;
cristya96f2492011-12-14 18:25:41 +00001582 kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
1583 kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
1584 if (kernel_info->values == (MagickRealType *) NULL)
cristy41cbe682011-07-15 19:12:37 +00001585 {
1586 kernel_info=DestroyKernelInfo(kernel_info);
1587 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1588 }
1589 j=(ssize_t) kernel_info->width/2;
1590 i=0;
1591 for (v=(-j); v <= j; v++)
1592 {
1593 for (u=(-j); u <= j; u++)
1594 {
1595 kernel_info->values[i]=(-1.0);
1596 i++;
1597 }
1598 }
1599 kernel_info->values[i/2]=(double) (width*width-1.0);
anthony736a1602011-10-06 12:38:17 +00001600 kernel_info->bias=image->bias; /* FUTURE: User bias on a edge image? */
cristy5e6be1e2011-07-16 01:23:39 +00001601 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001602 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001603 return(edge_image);
1604}
1605
1606/*
1607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608% %
1609% %
1610% %
1611% E m b o s s I m a g e %
1612% %
1613% %
1614% %
1615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616%
1617% EmbossImage() returns a grayscale image with a three-dimensional effect.
1618% We convolve the image with a Gaussian operator of the given radius and
1619% standard deviation (sigma). For reasonable results, radius should be
1620% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1621% radius for you.
1622%
1623% The format of the EmbossImage method is:
1624%
1625% Image *EmbossImage(const Image *image,const double radius,
1626% const double sigma,ExceptionInfo *exception)
1627%
1628% A description of each parameter follows:
1629%
1630% o image: the image.
1631%
1632% o radius: the radius of the pixel neighborhood.
1633%
1634% o sigma: the standard deviation of the Gaussian, in pixels.
1635%
1636% o exception: return any errors or warnings in this structure.
1637%
1638*/
1639MagickExport Image *EmbossImage(const Image *image,const double radius,
1640 const double sigma,ExceptionInfo *exception)
1641{
cristy3ed852e2009-09-05 21:47:34 +00001642 Image
1643 *emboss_image;
1644
cristy41cbe682011-07-15 19:12:37 +00001645 KernelInfo
1646 *kernel_info;
1647
cristybb503372010-05-27 20:51:26 +00001648 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001649 i;
1650
cristybb503372010-05-27 20:51:26 +00001651 size_t
cristy3ed852e2009-09-05 21:47:34 +00001652 width;
1653
cristy117ff172010-08-15 21:35:32 +00001654 ssize_t
1655 j,
1656 k,
1657 u,
1658 v;
1659
cristy41cbe682011-07-15 19:12:37 +00001660 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001661 assert(image->signature == MagickSignature);
1662 if (image->debug != MagickFalse)
1663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1664 assert(exception != (ExceptionInfo *) NULL);
1665 assert(exception->signature == MagickSignature);
cristyfc0ae532011-12-06 15:14:45 +00001666 width=GetOptimalKernelWidth1D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001667 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001668 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001669 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001670 kernel_info->width=width;
1671 kernel_info->height=width;
cristya96f2492011-12-14 18:25:41 +00001672 kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
1673 kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
1674 if (kernel_info->values == (MagickRealType *) NULL)
cristy41cbe682011-07-15 19:12:37 +00001675 {
1676 kernel_info=DestroyKernelInfo(kernel_info);
1677 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1678 }
1679 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001680 k=j;
1681 i=0;
1682 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001683 {
cristy47e00502009-12-17 19:19:57 +00001684 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001685 {
cristy41cbe682011-07-15 19:12:37 +00001686 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001687 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001688 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001689 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001690 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001691 i++;
1692 }
cristy47e00502009-12-17 19:19:57 +00001693 k--;
cristy3ed852e2009-09-05 21:47:34 +00001694 }
anthony736a1602011-10-06 12:38:17 +00001695 kernel_info->bias=image->bias; /* FUTURE: user bias on an edge image */
cristy5e6be1e2011-07-16 01:23:39 +00001696 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001697 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001698 if (emboss_image != (Image *) NULL)
cristy6d8c3d72011-08-22 01:20:01 +00001699 (void) EqualizeImage(emboss_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001700 return(emboss_image);
1701}
1702
1703/*
1704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1705% %
1706% %
1707% %
1708% G a u s s i a n B l u r I m a g e %
1709% %
1710% %
1711% %
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713%
1714% GaussianBlurImage() blurs an image. We convolve the image with a
1715% Gaussian operator of the given radius and standard deviation (sigma).
1716% For reasonable results, the radius should be larger than sigma. Use a
1717% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1718%
1719% The format of the GaussianBlurImage method is:
1720%
1721% Image *GaussianBlurImage(const Image *image,onst double radius,
cristyd89705a2012-01-20 02:52:24 +00001722% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001723%
1724% A description of each parameter follows:
1725%
1726% o image: the image.
1727%
cristy3ed852e2009-09-05 21:47:34 +00001728% o radius: the radius of the Gaussian, in pixels, not counting the center
1729% pixel.
1730%
1731% o sigma: the standard deviation of the Gaussian, in pixels.
1732%
1733% o exception: return any errors or warnings in this structure.
1734%
1735*/
cristy41cbe682011-07-15 19:12:37 +00001736MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
cristyd89705a2012-01-20 02:52:24 +00001737 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001738{
cristy3ed852e2009-09-05 21:47:34 +00001739 Image
1740 *blur_image;
1741
cristy41cbe682011-07-15 19:12:37 +00001742 KernelInfo
1743 *kernel_info;
1744
cristybb503372010-05-27 20:51:26 +00001745 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001746 i;
1747
cristybb503372010-05-27 20:51:26 +00001748 size_t
cristy3ed852e2009-09-05 21:47:34 +00001749 width;
1750
cristy117ff172010-08-15 21:35:32 +00001751 ssize_t
1752 j,
1753 u,
1754 v;
1755
cristy3ed852e2009-09-05 21:47:34 +00001756 assert(image != (const Image *) NULL);
1757 assert(image->signature == MagickSignature);
1758 if (image->debug != MagickFalse)
1759 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1760 assert(exception != (ExceptionInfo *) NULL);
1761 assert(exception->signature == MagickSignature);
1762 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001763 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001764 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001765 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001766 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1767 kernel_info->width=width;
1768 kernel_info->height=width;
1769 kernel_info->signature=MagickSignature;
cristya96f2492011-12-14 18:25:41 +00001770 kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
1771 kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
1772 if (kernel_info->values == (MagickRealType *) NULL)
cristy41cbe682011-07-15 19:12:37 +00001773 {
1774 kernel_info=DestroyKernelInfo(kernel_info);
1775 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1776 }
1777 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00001778 i=0;
cristy47e00502009-12-17 19:19:57 +00001779 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001780 {
cristy47e00502009-12-17 19:19:57 +00001781 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00001782 {
1783 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
1784 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
1785 i++;
1786 }
cristy3ed852e2009-09-05 21:47:34 +00001787 }
cristy5e6be1e2011-07-16 01:23:39 +00001788 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001789 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001790 return(blur_image);
1791}
1792
1793/*
1794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1795% %
1796% %
1797% %
cristy3ed852e2009-09-05 21:47:34 +00001798% M o t i o n B l u r I m a g e %
1799% %
1800% %
1801% %
1802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1803%
1804% MotionBlurImage() simulates motion blur. We convolve the image with a
1805% Gaussian operator of the given radius and standard deviation (sigma).
1806% For reasonable results, radius should be larger than sigma. Use a
1807% radius of 0 and MotionBlurImage() selects a suitable radius for you.
1808% Angle gives the angle of the blurring motion.
1809%
1810% Andrew Protano contributed this effect.
1811%
1812% The format of the MotionBlurImage method is:
1813%
1814% Image *MotionBlurImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00001815% const double sigma,const double angle,const double bias,
1816% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001817%
1818% A description of each parameter follows:
1819%
1820% o image: the image.
1821%
cristy3ed852e2009-09-05 21:47:34 +00001822% o radius: the radius of the Gaussian, in pixels, not counting
1823% the center pixel.
1824%
1825% o sigma: the standard deviation of the Gaussian, in pixels.
1826%
cristycee97112010-05-28 00:44:52 +00001827% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00001828%
cristyf7ef0252011-09-09 14:50:06 +00001829% o bias: the bias.
1830%
cristy3ed852e2009-09-05 21:47:34 +00001831% o exception: return any errors or warnings in this structure.
1832%
1833*/
1834
cristybb503372010-05-27 20:51:26 +00001835static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00001836{
cristy3ed852e2009-09-05 21:47:34 +00001837 double
cristy47e00502009-12-17 19:19:57 +00001838 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00001839 normalize;
1840
cristybb503372010-05-27 20:51:26 +00001841 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001842 i;
1843
1844 /*
cristy47e00502009-12-17 19:19:57 +00001845 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00001846 */
1847 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy8b49f382012-01-17 03:11:03 +00001848 kernel=(double *) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
cristy3ed852e2009-09-05 21:47:34 +00001849 if (kernel == (double *) NULL)
1850 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001851 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00001852 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00001853 {
cristy4205a3c2010-09-12 20:19:59 +00001854 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
1855 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00001856 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00001857 }
cristybb503372010-05-27 20:51:26 +00001858 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001859 kernel[i]/=normalize;
1860 return(kernel);
1861}
1862
cristya63e4a92011-09-09 00:47:59 +00001863MagickExport Image *MotionBlurImage(const Image *image,const double radius,
cristyf7ef0252011-09-09 14:50:06 +00001864 const double sigma,const double angle,const double bias,
1865 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001866{
cristyc4c8d132010-01-07 01:58:38 +00001867 CacheView
1868 *blur_view,
cristy8b49f382012-01-17 03:11:03 +00001869 *image_view,
1870 *motion_view;
cristyc4c8d132010-01-07 01:58:38 +00001871
cristy3ed852e2009-09-05 21:47:34 +00001872 double
1873 *kernel;
1874
1875 Image
1876 *blur_image;
1877
cristy3ed852e2009-09-05 21:47:34 +00001878 MagickBooleanType
1879 status;
1880
cristybb503372010-05-27 20:51:26 +00001881 MagickOffsetType
1882 progress;
1883
cristy3ed852e2009-09-05 21:47:34 +00001884 OffsetInfo
1885 *offset;
1886
1887 PointInfo
1888 point;
1889
cristybb503372010-05-27 20:51:26 +00001890 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001891 i;
1892
cristybb503372010-05-27 20:51:26 +00001893 size_t
cristy3ed852e2009-09-05 21:47:34 +00001894 width;
1895
cristybb503372010-05-27 20:51:26 +00001896 ssize_t
1897 y;
1898
cristy3ed852e2009-09-05 21:47:34 +00001899 assert(image != (Image *) NULL);
1900 assert(image->signature == MagickSignature);
1901 if (image->debug != MagickFalse)
1902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1903 assert(exception != (ExceptionInfo *) NULL);
1904 width=GetOptimalKernelWidth1D(radius,sigma);
1905 kernel=GetMotionBlurKernel(width,sigma);
1906 if (kernel == (double *) NULL)
1907 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1908 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
1909 if (offset == (OffsetInfo *) NULL)
1910 {
cristy8b49f382012-01-17 03:11:03 +00001911 kernel=(double *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001912 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1913 }
cristy1e7aa312011-09-10 20:01:36 +00001914 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001915 if (blur_image == (Image *) NULL)
1916 {
cristy8b49f382012-01-17 03:11:03 +00001917 kernel=(double *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001918 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1919 return((Image *) NULL);
1920 }
cristy574cc262011-08-05 01:23:58 +00001921 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001922 {
cristy8b49f382012-01-17 03:11:03 +00001923 kernel=(double *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001924 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
cristy3ed852e2009-09-05 21:47:34 +00001925 blur_image=DestroyImage(blur_image);
1926 return((Image *) NULL);
1927 }
1928 point.x=(double) width*sin(DegreesToRadians(angle));
1929 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00001930 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001931 {
cristybb503372010-05-27 20:51:26 +00001932 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
1933 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00001934 }
1935 /*
1936 Motion blur image.
1937 */
1938 status=MagickTrue;
1939 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00001940 image_view=AcquireCacheView(image);
cristy8b49f382012-01-17 03:11:03 +00001941 motion_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00001942 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00001943#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00001944 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001945#endif
cristybb503372010-05-27 20:51:26 +00001946 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001947 {
cristyf7ef0252011-09-09 14:50:06 +00001948 register const Quantum
1949 *restrict p;
1950
cristy4c08aed2011-07-01 19:47:50 +00001951 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001952 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001953
cristy117ff172010-08-15 21:35:32 +00001954 register ssize_t
1955 x;
1956
cristy3ed852e2009-09-05 21:47:34 +00001957 if (status == MagickFalse)
1958 continue;
cristy8b49f382012-01-17 03:11:03 +00001959 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1960 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00001961 exception);
cristyf7ef0252011-09-09 14:50:06 +00001962 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001963 {
1964 status=MagickFalse;
1965 continue;
1966 }
cristybb503372010-05-27 20:51:26 +00001967 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001968 {
cristybb503372010-05-27 20:51:26 +00001969 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001970 i;
1971
cristyf7ef0252011-09-09 14:50:06 +00001972 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1973 {
1974 MagickRealType
1975 alpha,
1976 gamma,
1977 pixel;
cristy3ed852e2009-09-05 21:47:34 +00001978
cristyf7ef0252011-09-09 14:50:06 +00001979 PixelChannel
1980 channel;
1981
1982 PixelTrait
1983 blur_traits,
1984 traits;
1985
1986 register const Quantum
1987 *restrict r;
1988
1989 register double
1990 *restrict k;
1991
1992 register ssize_t
1993 j;
1994
cristye2a912b2011-12-05 20:02:07 +00001995 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00001996 traits=GetPixelChannelMapTraits(image,channel);
cristyf7ef0252011-09-09 14:50:06 +00001997 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1998 if ((traits == UndefinedPixelTrait) ||
1999 (blur_traits == UndefinedPixelTrait))
2000 continue;
cristyd09f8802012-02-04 16:44:10 +00002001 if (((blur_traits & CopyPixelTrait) != 0) ||
2002 (GetPixelMask(image,p) != 0))
cristy3ed852e2009-09-05 21:47:34 +00002003 {
cristy0beccfa2011-09-25 20:47:53 +00002004 SetPixelChannel(blur_image,channel,p[i],q);
cristyf7ef0252011-09-09 14:50:06 +00002005 continue;
cristy3ed852e2009-09-05 21:47:34 +00002006 }
cristyf7ef0252011-09-09 14:50:06 +00002007 k=kernel;
2008 pixel=bias;
2009 if ((blur_traits & BlendPixelTrait) == 0)
2010 {
2011 for (j=0; j < (ssize_t) width; j++)
2012 {
cristy8b49f382012-01-17 03:11:03 +00002013 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
cristyf7ef0252011-09-09 14:50:06 +00002014 offset[j].y,1,1,exception);
2015 if (r == (const Quantum *) NULL)
2016 {
2017 status=MagickFalse;
2018 continue;
2019 }
2020 pixel+=(*k)*r[i];
2021 k++;
2022 }
cristy0beccfa2011-09-25 20:47:53 +00002023 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
cristyf7ef0252011-09-09 14:50:06 +00002024 continue;
2025 }
2026 alpha=0.0;
2027 gamma=0.0;
2028 for (j=0; j < (ssize_t) width; j++)
2029 {
cristy8b49f382012-01-17 03:11:03 +00002030 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
cristyf7ef0252011-09-09 14:50:06 +00002031 1,exception);
2032 if (r == (const Quantum *) NULL)
2033 {
2034 status=MagickFalse;
2035 continue;
2036 }
2037 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,r));
2038 pixel+=(*k)*alpha*r[i];
2039 gamma+=(*k)*alpha;
2040 k++;
cristy3ed852e2009-09-05 21:47:34 +00002041 }
cristyf7ef0252011-09-09 14:50:06 +00002042 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00002043 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristyf7ef0252011-09-09 14:50:06 +00002044 }
2045 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00002046 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002047 }
2048 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2049 status=MagickFalse;
2050 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2051 {
2052 MagickBooleanType
2053 proceed;
2054
cristyb557a152011-02-22 12:14:30 +00002055#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00002056 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002057#endif
2058 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2059 if (proceed == MagickFalse)
2060 status=MagickFalse;
2061 }
2062 }
2063 blur_view=DestroyCacheView(blur_view);
cristy8b49f382012-01-17 03:11:03 +00002064 motion_view=DestroyCacheView(motion_view);
cristy3ed852e2009-09-05 21:47:34 +00002065 image_view=DestroyCacheView(image_view);
cristy8b49f382012-01-17 03:11:03 +00002066 kernel=(double *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002067 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2068 if (status == MagickFalse)
2069 blur_image=DestroyImage(blur_image);
2070 return(blur_image);
2071}
2072
2073/*
2074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2075% %
2076% %
2077% %
2078% P r e v i e w I m a g e %
2079% %
2080% %
2081% %
2082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2083%
2084% PreviewImage() tiles 9 thumbnails of the specified image with an image
2085% processing operation applied with varying parameters. This may be helpful
2086% pin-pointing an appropriate parameter for a particular image processing
2087% operation.
2088%
2089% The format of the PreviewImages method is:
2090%
2091% Image *PreviewImages(const Image *image,const PreviewType preview,
2092% ExceptionInfo *exception)
2093%
2094% A description of each parameter follows:
2095%
2096% o image: the image.
2097%
2098% o preview: the image processing operation.
2099%
2100% o exception: return any errors or warnings in this structure.
2101%
2102*/
2103MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2104 ExceptionInfo *exception)
2105{
2106#define NumberTiles 9
2107#define PreviewImageTag "Preview/Image"
2108#define DefaultPreviewGeometry "204x204+10+10"
2109
2110 char
2111 factor[MaxTextExtent],
2112 label[MaxTextExtent];
2113
2114 double
2115 degrees,
2116 gamma,
2117 percentage,
2118 radius,
2119 sigma,
2120 threshold;
2121
2122 Image
2123 *images,
2124 *montage_image,
2125 *preview_image,
2126 *thumbnail;
2127
2128 ImageInfo
2129 *preview_info;
2130
cristy3ed852e2009-09-05 21:47:34 +00002131 MagickBooleanType
2132 proceed;
2133
2134 MontageInfo
2135 *montage_info;
2136
2137 QuantizeInfo
2138 quantize_info;
2139
2140 RectangleInfo
2141 geometry;
2142
cristybb503372010-05-27 20:51:26 +00002143 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002144 i,
2145 x;
2146
cristybb503372010-05-27 20:51:26 +00002147 size_t
cristy3ed852e2009-09-05 21:47:34 +00002148 colors;
2149
cristy117ff172010-08-15 21:35:32 +00002150 ssize_t
2151 y;
2152
cristy3ed852e2009-09-05 21:47:34 +00002153 /*
2154 Open output image file.
2155 */
2156 assert(image != (Image *) NULL);
2157 assert(image->signature == MagickSignature);
2158 if (image->debug != MagickFalse)
2159 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2160 colors=2;
2161 degrees=0.0;
2162 gamma=(-0.2f);
2163 preview_info=AcquireImageInfo();
2164 SetGeometry(image,&geometry);
2165 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2166 &geometry.width,&geometry.height);
2167 images=NewImageList();
2168 percentage=12.5;
2169 GetQuantizeInfo(&quantize_info);
2170 radius=0.0;
2171 sigma=1.0;
2172 threshold=0.0;
2173 x=0;
2174 y=0;
2175 for (i=0; i < NumberTiles; i++)
2176 {
2177 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2178 if (thumbnail == (Image *) NULL)
2179 break;
2180 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2181 (void *) NULL);
cristyd15e6592011-10-15 00:13:06 +00002182 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002183 if (i == (NumberTiles/2))
2184 {
cristy9950d572011-10-01 18:22:35 +00002185 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2186 &thumbnail->matte_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00002187 AppendImageToList(&images,thumbnail);
2188 continue;
2189 }
2190 switch (preview)
2191 {
2192 case RotatePreview:
2193 {
2194 degrees+=45.0;
2195 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002196 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002197 break;
2198 }
2199 case ShearPreview:
2200 {
2201 degrees+=5.0;
2202 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002203 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002204 degrees,2.0*degrees);
2205 break;
2206 }
2207 case RollPreview:
2208 {
cristybb503372010-05-27 20:51:26 +00002209 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2210 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002211 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002212 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002213 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002214 break;
2215 }
2216 case HuePreview:
2217 {
2218 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2219 if (preview_image == (Image *) NULL)
2220 break;
cristyb51dff52011-05-19 16:55:47 +00002221 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002222 2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002223 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002224 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002225 break;
2226 }
2227 case SaturationPreview:
2228 {
2229 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2230 if (preview_image == (Image *) NULL)
2231 break;
cristyb51dff52011-05-19 16:55:47 +00002232 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002233 2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002234 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002235 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002236 break;
2237 }
2238 case BrightnessPreview:
2239 {
2240 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2241 if (preview_image == (Image *) NULL)
2242 break;
cristyb51dff52011-05-19 16:55:47 +00002243 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002244 (void) ModulateImage(preview_image,factor,exception);
cristyb51dff52011-05-19 16:55:47 +00002245 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002246 break;
2247 }
2248 case GammaPreview:
2249 default:
2250 {
2251 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2252 if (preview_image == (Image *) NULL)
2253 break;
2254 gamma+=0.4f;
cristyb3e7c6c2011-07-24 01:43:55 +00002255 (void) GammaImage(preview_image,gamma,exception);
cristyb51dff52011-05-19 16:55:47 +00002256 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002257 break;
2258 }
2259 case SpiffPreview:
2260 {
2261 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2262 if (preview_image != (Image *) NULL)
2263 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002264 (void) ContrastImage(preview_image,MagickTrue,exception);
cristyb51dff52011-05-19 16:55:47 +00002265 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002266 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002267 break;
2268 }
2269 case DullPreview:
2270 {
2271 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2272 if (preview_image == (Image *) NULL)
2273 break;
2274 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002275 (void) ContrastImage(preview_image,MagickFalse,exception);
cristyb51dff52011-05-19 16:55:47 +00002276 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002277 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002278 break;
2279 }
2280 case GrayscalePreview:
2281 {
2282 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2283 if (preview_image == (Image *) NULL)
2284 break;
2285 colors<<=1;
2286 quantize_info.number_colors=colors;
2287 quantize_info.colorspace=GRAYColorspace;
cristy018f07f2011-09-04 21:15:19 +00002288 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristyb51dff52011-05-19 16:55:47 +00002289 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002290 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002291 break;
2292 }
2293 case QuantizePreview:
2294 {
2295 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2296 if (preview_image == (Image *) NULL)
2297 break;
2298 colors<<=1;
2299 quantize_info.number_colors=colors;
cristy018f07f2011-09-04 21:15:19 +00002300 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristyb51dff52011-05-19 16:55:47 +00002301 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002302 colors);
cristy3ed852e2009-09-05 21:47:34 +00002303 break;
2304 }
2305 case DespecklePreview:
2306 {
2307 for (x=0; x < (i-1); x++)
2308 {
2309 preview_image=DespeckleImage(thumbnail,exception);
2310 if (preview_image == (Image *) NULL)
2311 break;
2312 thumbnail=DestroyImage(thumbnail);
2313 thumbnail=preview_image;
2314 }
2315 preview_image=DespeckleImage(thumbnail,exception);
2316 if (preview_image == (Image *) NULL)
2317 break;
cristyb51dff52011-05-19 16:55:47 +00002318 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002319 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002320 break;
2321 }
2322 case ReduceNoisePreview:
2323 {
cristy95c38342011-03-18 22:39:51 +00002324 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2325 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002326 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002327 break;
2328 }
2329 case AddNoisePreview:
2330 {
2331 switch ((int) i)
2332 {
2333 case 0:
2334 {
2335 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2336 break;
2337 }
2338 case 1:
2339 {
2340 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2341 break;
2342 }
2343 case 2:
2344 {
2345 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2346 break;
2347 }
2348 case 3:
2349 {
2350 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2351 break;
2352 }
2353 case 4:
2354 {
2355 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2356 break;
2357 }
2358 case 5:
2359 {
2360 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2361 break;
2362 }
2363 default:
2364 {
2365 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2366 break;
2367 }
2368 }
cristyd76c51e2011-03-26 00:21:26 +00002369 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2370 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002371 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002372 break;
2373 }
2374 case SharpenPreview:
2375 {
anthony736a1602011-10-06 12:38:17 +00002376 /* FUTURE: user bias on sharpen! This is non-sensical! */
cristy05c0c9a2011-09-05 23:16:13 +00002377 preview_image=SharpenImage(thumbnail,radius,sigma,image->bias,
2378 exception);
cristyb51dff52011-05-19 16:55:47 +00002379 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002380 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002381 break;
2382 }
2383 case BlurPreview:
2384 {
anthony736a1602011-10-06 12:38:17 +00002385 /* FUTURE: user bias on blur! This is non-sensical! */
cristy05c0c9a2011-09-05 23:16:13 +00002386 preview_image=BlurImage(thumbnail,radius,sigma,image->bias,exception);
cristyb51dff52011-05-19 16:55:47 +00002387 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002388 sigma);
2389 break;
2390 }
2391 case ThresholdPreview:
2392 {
2393 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2394 if (preview_image == (Image *) NULL)
2395 break;
cristye941a752011-10-15 01:52:48 +00002396 (void) BilevelImage(thumbnail,(double) (percentage*((MagickRealType)
2397 QuantumRange+1.0))/100.0,exception);
cristyb51dff52011-05-19 16:55:47 +00002398 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002399 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2400 break;
2401 }
2402 case EdgeDetectPreview:
2403 {
cristy8ae632d2011-09-05 17:29:53 +00002404 preview_image=EdgeImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002405 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002406 break;
2407 }
2408 case SpreadPreview:
2409 {
cristy5c4e2582011-09-11 19:21:03 +00002410 preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2411 exception);
cristyb51dff52011-05-19 16:55:47 +00002412 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002413 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002414 break;
2415 }
2416 case SolarizePreview:
2417 {
2418 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2419 if (preview_image == (Image *) NULL)
2420 break;
2421 (void) SolarizeImage(preview_image,(double) QuantumRange*
cristy5cbc0162011-08-29 00:36:28 +00002422 percentage/100.0,exception);
cristyb51dff52011-05-19 16:55:47 +00002423 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002424 (QuantumRange*percentage)/100.0);
2425 break;
2426 }
2427 case ShadePreview:
2428 {
2429 degrees+=10.0;
2430 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2431 exception);
cristyb51dff52011-05-19 16:55:47 +00002432 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002433 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002434 break;
2435 }
2436 case RaisePreview:
2437 {
2438 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2439 if (preview_image == (Image *) NULL)
2440 break;
cristybb503372010-05-27 20:51:26 +00002441 geometry.width=(size_t) (2*i+2);
2442 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002443 geometry.x=i/2;
2444 geometry.y=i/2;
cristy6170ac32011-08-28 14:15:37 +00002445 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
cristyb51dff52011-05-19 16:55:47 +00002446 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002447 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002448 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002449 break;
2450 }
2451 case SegmentPreview:
2452 {
2453 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2454 if (preview_image == (Image *) NULL)
2455 break;
2456 threshold+=0.4f;
2457 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
cristy018f07f2011-09-04 21:15:19 +00002458 threshold,exception);
cristyb51dff52011-05-19 16:55:47 +00002459 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002460 threshold,threshold);
2461 break;
2462 }
2463 case SwirlPreview:
2464 {
cristy76f512e2011-09-12 01:26:56 +00002465 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2466 exception);
cristyb51dff52011-05-19 16:55:47 +00002467 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002468 degrees+=45.0;
2469 break;
2470 }
2471 case ImplodePreview:
2472 {
2473 degrees+=0.1f;
cristy76f512e2011-09-12 01:26:56 +00002474 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2475 exception);
cristyb51dff52011-05-19 16:55:47 +00002476 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002477 break;
2478 }
2479 case WavePreview:
2480 {
2481 degrees+=5.0f;
cristy5c4e2582011-09-11 19:21:03 +00002482 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2483 image->interpolate,exception);
cristyb51dff52011-05-19 16:55:47 +00002484 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002485 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002486 break;
2487 }
2488 case OilPaintPreview:
2489 {
cristy14973ba2011-08-27 23:48:07 +00002490 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2491 exception);
2492 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2493 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002494 break;
2495 }
2496 case CharcoalDrawingPreview:
2497 {
anthony736a1602011-10-06 12:38:17 +00002498 /* FUTURE: user bias on charcoal! This is non-sensical! */
cristy3ed852e2009-09-05 21:47:34 +00002499 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
cristy05c0c9a2011-09-05 23:16:13 +00002500 image->bias,exception);
cristyb51dff52011-05-19 16:55:47 +00002501 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002502 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002503 break;
2504 }
2505 case JPEGPreview:
2506 {
2507 char
2508 filename[MaxTextExtent];
2509
2510 int
2511 file;
2512
2513 MagickBooleanType
2514 status;
2515
2516 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2517 if (preview_image == (Image *) NULL)
2518 break;
cristybb503372010-05-27 20:51:26 +00002519 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002520 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002521 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002522 file=AcquireUniqueFileResource(filename);
2523 if (file != -1)
2524 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002525 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002526 "jpeg:%s",filename);
cristy6f9e0d32011-08-28 16:32:09 +00002527 status=WriteImage(preview_info,preview_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002528 if (status != MagickFalse)
2529 {
2530 Image
2531 *quality_image;
2532
2533 (void) CopyMagickString(preview_info->filename,
2534 preview_image->filename,MaxTextExtent);
2535 quality_image=ReadImage(preview_info,exception);
2536 if (quality_image != (Image *) NULL)
2537 {
2538 preview_image=DestroyImage(preview_image);
2539 preview_image=quality_image;
2540 }
2541 }
2542 (void) RelinquishUniqueFileResource(preview_image->filename);
2543 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002544 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002545 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2546 1024.0/1024.0);
2547 else
2548 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002549 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002550 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002551 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002552 else
cristyb51dff52011-05-19 16:55:47 +00002553 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002554 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002555 break;
2556 }
2557 }
2558 thumbnail=DestroyImage(thumbnail);
2559 percentage+=12.5;
2560 radius+=0.5;
2561 sigma+=0.25;
2562 if (preview_image == (Image *) NULL)
2563 break;
2564 (void) DeleteImageProperty(preview_image,"label");
cristyd15e6592011-10-15 00:13:06 +00002565 (void) SetImageProperty(preview_image,"label",label,exception);
cristy3ed852e2009-09-05 21:47:34 +00002566 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002567 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2568 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002569 if (proceed == MagickFalse)
2570 break;
2571 }
2572 if (images == (Image *) NULL)
2573 {
2574 preview_info=DestroyImageInfo(preview_info);
2575 return((Image *) NULL);
2576 }
2577 /*
2578 Create the montage.
2579 */
2580 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2581 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2582 montage_info->shadow=MagickTrue;
2583 (void) CloneString(&montage_info->tile,"3x3");
2584 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2585 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2586 montage_image=MontageImages(images,montage_info,exception);
2587 montage_info=DestroyMontageInfo(montage_info);
2588 images=DestroyImageList(images);
2589 if (montage_image == (Image *) NULL)
2590 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2591 if (montage_image->montage != (char *) NULL)
2592 {
2593 /*
2594 Free image directory.
2595 */
2596 montage_image->montage=(char *) RelinquishMagickMemory(
2597 montage_image->montage);
2598 if (image->directory != (char *) NULL)
2599 montage_image->directory=(char *) RelinquishMagickMemory(
2600 montage_image->directory);
2601 }
2602 preview_info=DestroyImageInfo(preview_info);
2603 return(montage_image);
2604}
2605
2606/*
2607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2608% %
2609% %
2610% %
2611% R a d i a l B l u r I m a g e %
2612% %
2613% %
2614% %
2615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2616%
2617% RadialBlurImage() applies a radial blur to the image.
2618%
2619% Andrew Protano contributed this effect.
2620%
2621% The format of the RadialBlurImage method is:
2622%
2623% Image *RadialBlurImage(const Image *image,const double angle,
cristy6435bd92011-09-10 02:10:07 +00002624% const double blur,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002625%
2626% A description of each parameter follows:
2627%
2628% o image: the image.
2629%
cristy3ed852e2009-09-05 21:47:34 +00002630% o angle: the angle of the radial blur.
2631%
cristy6435bd92011-09-10 02:10:07 +00002632% o blur: the blur.
2633%
cristy3ed852e2009-09-05 21:47:34 +00002634% o exception: return any errors or warnings in this structure.
2635%
2636*/
cristy4282c702011-11-21 00:01:06 +00002637MagickExport Image *RadialBlurImage(const Image *image,const double angle,
2638 const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002639{
cristyc4c8d132010-01-07 01:58:38 +00002640 CacheView
2641 *blur_view,
cristy8b49f382012-01-17 03:11:03 +00002642 *image_view,
2643 *radial_view;
cristyc4c8d132010-01-07 01:58:38 +00002644
cristy3ed852e2009-09-05 21:47:34 +00002645 Image
2646 *blur_image;
2647
cristy3ed852e2009-09-05 21:47:34 +00002648 MagickBooleanType
2649 status;
2650
cristybb503372010-05-27 20:51:26 +00002651 MagickOffsetType
2652 progress;
2653
cristy3ed852e2009-09-05 21:47:34 +00002654 MagickRealType
2655 blur_radius,
2656 *cos_theta,
2657 offset,
2658 *sin_theta,
2659 theta;
2660
2661 PointInfo
2662 blur_center;
2663
cristybb503372010-05-27 20:51:26 +00002664 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002665 i;
2666
cristybb503372010-05-27 20:51:26 +00002667 size_t
cristy3ed852e2009-09-05 21:47:34 +00002668 n;
2669
cristybb503372010-05-27 20:51:26 +00002670 ssize_t
2671 y;
2672
cristy3ed852e2009-09-05 21:47:34 +00002673 /*
2674 Allocate blur image.
2675 */
2676 assert(image != (Image *) NULL);
2677 assert(image->signature == MagickSignature);
2678 if (image->debug != MagickFalse)
2679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2680 assert(exception != (ExceptionInfo *) NULL);
2681 assert(exception->signature == MagickSignature);
cristy1e7aa312011-09-10 20:01:36 +00002682 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002683 if (blur_image == (Image *) NULL)
2684 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002685 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002686 {
cristy3ed852e2009-09-05 21:47:34 +00002687 blur_image=DestroyImage(blur_image);
2688 return((Image *) NULL);
2689 }
2690 blur_center.x=(double) image->columns/2.0;
2691 blur_center.y=(double) image->rows/2.0;
2692 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002693 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002694 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2695 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2696 sizeof(*cos_theta));
2697 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2698 sizeof(*sin_theta));
2699 if ((cos_theta == (MagickRealType *) NULL) ||
2700 (sin_theta == (MagickRealType *) NULL))
2701 {
2702 blur_image=DestroyImage(blur_image);
2703 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2704 }
2705 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002706 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002707 {
2708 cos_theta[i]=cos((double) (theta*i-offset));
2709 sin_theta[i]=sin((double) (theta*i-offset));
2710 }
2711 /*
2712 Radial blur image.
2713 */
2714 status=MagickTrue;
2715 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00002716 image_view=AcquireCacheView(image);
cristy8b49f382012-01-17 03:11:03 +00002717 radial_view=AcquireCacheView(image);
cristy3ed852e2009-09-05 21:47:34 +00002718 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002719#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002720 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002721#endif
cristy1e7aa312011-09-10 20:01:36 +00002722 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002723 {
cristy1e7aa312011-09-10 20:01:36 +00002724 register const Quantum
2725 *restrict p;
2726
cristy4c08aed2011-07-01 19:47:50 +00002727 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002728 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002729
cristy117ff172010-08-15 21:35:32 +00002730 register ssize_t
2731 x;
2732
cristy3ed852e2009-09-05 21:47:34 +00002733 if (status == MagickFalse)
2734 continue;
cristy8b49f382012-01-17 03:11:03 +00002735 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2736 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00002737 exception);
cristy1e7aa312011-09-10 20:01:36 +00002738 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002739 {
2740 status=MagickFalse;
2741 continue;
2742 }
cristy1e7aa312011-09-10 20:01:36 +00002743 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
cristy3ed852e2009-09-05 21:47:34 +00002745 MagickRealType
cristy3ed852e2009-09-05 21:47:34 +00002746 radius;
2747
cristy3ed852e2009-09-05 21:47:34 +00002748 PointInfo
2749 center;
2750
cristybb503372010-05-27 20:51:26 +00002751 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002752 i;
2753
cristybb503372010-05-27 20:51:26 +00002754 size_t
cristy3ed852e2009-09-05 21:47:34 +00002755 step;
2756
2757 center.x=(double) x-blur_center.x;
2758 center.y=(double) y-blur_center.y;
2759 radius=hypot((double) center.x,center.y);
2760 if (radius == 0)
2761 step=1;
2762 else
2763 {
cristybb503372010-05-27 20:51:26 +00002764 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002765 if (step == 0)
2766 step=1;
2767 else
2768 if (step >= n)
2769 step=n-1;
2770 }
cristy1e7aa312011-09-10 20:01:36 +00002771 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2772 {
2773 MagickRealType
2774 gamma,
2775 pixel;
cristy3ed852e2009-09-05 21:47:34 +00002776
cristy1e7aa312011-09-10 20:01:36 +00002777 PixelChannel
2778 channel;
2779
2780 PixelTrait
2781 blur_traits,
2782 traits;
2783
2784 register const Quantum
2785 *restrict r;
2786
2787 register ssize_t
2788 j;
2789
cristye2a912b2011-12-05 20:02:07 +00002790 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00002791 traits=GetPixelChannelMapTraits(image,channel);
cristy1e7aa312011-09-10 20:01:36 +00002792 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
2793 if ((traits == UndefinedPixelTrait) ||
2794 (blur_traits == UndefinedPixelTrait))
2795 continue;
cristyd09f8802012-02-04 16:44:10 +00002796 if (((blur_traits & CopyPixelTrait) != 0) ||
2797 (GetPixelMask(image,p) != 0))
cristy3ed852e2009-09-05 21:47:34 +00002798 {
cristy0beccfa2011-09-25 20:47:53 +00002799 SetPixelChannel(blur_image,channel,p[i],q);
cristy1e7aa312011-09-10 20:01:36 +00002800 continue;
cristy3ed852e2009-09-05 21:47:34 +00002801 }
cristy1e7aa312011-09-10 20:01:36 +00002802 gamma=0.0;
2803 pixel=bias;
2804 if ((blur_traits & BlendPixelTrait) == 0)
2805 {
2806 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2807 {
cristy8b49f382012-01-17 03:11:03 +00002808 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
cristy1e7aa312011-09-10 20:01:36 +00002809 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2810 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2811 1,1,exception);
2812 if (r == (const Quantum *) NULL)
2813 {
2814 status=MagickFalse;
2815 continue;
2816 }
2817 pixel+=r[i];
2818 gamma++;
2819 }
2820 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00002821 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00002822 continue;
2823 }
2824 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2825 {
cristy8b49f382012-01-17 03:11:03 +00002826 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
cristy1e7aa312011-09-10 20:01:36 +00002827 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2828 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2829 1,1,exception);
2830 if (r == (const Quantum *) NULL)
2831 {
2832 status=MagickFalse;
2833 continue;
2834 }
2835 pixel+=GetPixelAlpha(image,r)*r[i];
2836 gamma+=GetPixelAlpha(image,r);
cristy3ed852e2009-09-05 21:47:34 +00002837 }
cristy1e7aa312011-09-10 20:01:36 +00002838 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00002839 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00002840 }
2841 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00002842 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002843 }
2844 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2845 status=MagickFalse;
2846 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2847 {
2848 MagickBooleanType
2849 proceed;
2850
cristyb5d5f722009-11-04 03:03:49 +00002851#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00002852 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002853#endif
2854 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2855 if (proceed == MagickFalse)
2856 status=MagickFalse;
2857 }
2858 }
2859 blur_view=DestroyCacheView(blur_view);
cristy8b49f382012-01-17 03:11:03 +00002860 radial_view=DestroyCacheView(radial_view);
cristy3ed852e2009-09-05 21:47:34 +00002861 image_view=DestroyCacheView(image_view);
2862 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
2863 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
2864 if (status == MagickFalse)
2865 blur_image=DestroyImage(blur_image);
2866 return(blur_image);
2867}
2868
2869/*
2870%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2871% %
2872% %
2873% %
cristy3ed852e2009-09-05 21:47:34 +00002874% S e l e c t i v e B l u r I m a g e %
2875% %
2876% %
2877% %
2878%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2879%
2880% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
2881% It is similar to the unsharpen mask that sharpens everything with contrast
2882% above a certain threshold.
2883%
2884% The format of the SelectiveBlurImage method is:
2885%
2886% Image *SelectiveBlurImage(const Image *image,const double radius,
cristy1e7aa312011-09-10 20:01:36 +00002887% const double sigma,const double threshold,const double bias,
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 radius: the radius of the Gaussian, in pixels, not counting the center
2895% pixel.
2896%
2897% o sigma: the standard deviation of the Gaussian, in pixels.
2898%
2899% o threshold: only pixels within this contrast threshold are included
2900% in the blur operation.
2901%
cristy1e7aa312011-09-10 20:01:36 +00002902% o bias: the bias.
2903%
cristy3ed852e2009-09-05 21:47:34 +00002904% o exception: return any errors or warnings in this structure.
2905%
2906*/
cristy4282c702011-11-21 00:01:06 +00002907MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
2908 const double sigma,const double threshold,const double bias,
2909 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002910{
2911#define SelectiveBlurImageTag "SelectiveBlur/Image"
2912
cristy47e00502009-12-17 19:19:57 +00002913 CacheView
2914 *blur_view,
2915 *image_view;
2916
cristy3ed852e2009-09-05 21:47:34 +00002917 double
cristy3ed852e2009-09-05 21:47:34 +00002918 *kernel;
2919
2920 Image
2921 *blur_image;
2922
cristy3ed852e2009-09-05 21:47:34 +00002923 MagickBooleanType
2924 status;
2925
cristybb503372010-05-27 20:51:26 +00002926 MagickOffsetType
2927 progress;
2928
cristybb503372010-05-27 20:51:26 +00002929 register ssize_t
cristy47e00502009-12-17 19:19:57 +00002930 i;
cristy3ed852e2009-09-05 21:47:34 +00002931
cristybb503372010-05-27 20:51:26 +00002932 size_t
cristy3ed852e2009-09-05 21:47:34 +00002933 width;
2934
cristybb503372010-05-27 20:51:26 +00002935 ssize_t
cristyc8523c12011-09-13 00:02:53 +00002936 center,
cristybb503372010-05-27 20:51:26 +00002937 j,
2938 u,
2939 v,
2940 y;
2941
cristy3ed852e2009-09-05 21:47:34 +00002942 /*
2943 Initialize blur image attributes.
2944 */
2945 assert(image != (Image *) NULL);
2946 assert(image->signature == MagickSignature);
2947 if (image->debug != MagickFalse)
2948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2949 assert(exception != (ExceptionInfo *) NULL);
2950 assert(exception->signature == MagickSignature);
2951 width=GetOptimalKernelWidth1D(radius,sigma);
cristy8b49f382012-01-17 03:11:03 +00002952 kernel=(double *) AcquireAlignedMemory((size_t) width,width*sizeof(*kernel));
cristy3ed852e2009-09-05 21:47:34 +00002953 if (kernel == (double *) NULL)
2954 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00002955 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00002956 i=0;
cristy47e00502009-12-17 19:19:57 +00002957 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00002958 {
cristy47e00502009-12-17 19:19:57 +00002959 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00002960 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
2961 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002962 }
2963 if (image->debug != MagickFalse)
2964 {
2965 char
2966 format[MaxTextExtent],
2967 *message;
2968
cristy117ff172010-08-15 21:35:32 +00002969 register const double
2970 *k;
2971
cristybb503372010-05-27 20:51:26 +00002972 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002973 u,
2974 v;
2975
cristy3ed852e2009-09-05 21:47:34 +00002976 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002977 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
2978 width);
cristy3ed852e2009-09-05 21:47:34 +00002979 message=AcquireString("");
2980 k=kernel;
cristybb503372010-05-27 20:51:26 +00002981 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00002982 {
2983 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00002984 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00002985 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00002986 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00002987 {
cristyb51dff52011-05-19 16:55:47 +00002988 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00002989 (void) ConcatenateString(&message,format);
2990 }
2991 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2992 }
2993 message=DestroyString(message);
2994 }
cristy1e7aa312011-09-10 20:01:36 +00002995 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002996 if (blur_image == (Image *) NULL)
2997 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002998 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
cristy3ed852e2009-09-05 21:47:34 +00003000 blur_image=DestroyImage(blur_image);
3001 return((Image *) NULL);
3002 }
3003 /*
3004 Threshold blur image.
3005 */
3006 status=MagickTrue;
3007 progress=0;
cristyc8523c12011-09-13 00:02:53 +00003008 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*(width/2L)+
3009 GetPixelChannels(image)*(width/2L));
cristy3ed852e2009-09-05 21:47:34 +00003010 image_view=AcquireCacheView(image);
3011 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003012#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003013 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003014#endif
cristybb503372010-05-27 20:51:26 +00003015 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003016 {
cristy4c08aed2011-07-01 19:47:50 +00003017 double
3018 contrast;
3019
cristy3ed852e2009-09-05 21:47:34 +00003020 MagickBooleanType
3021 sync;
3022
cristy4c08aed2011-07-01 19:47:50 +00003023 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003024 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003025
cristy4c08aed2011-07-01 19:47:50 +00003026 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003027 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003028
cristy117ff172010-08-15 21:35:32 +00003029 register ssize_t
3030 x;
3031
cristy3ed852e2009-09-05 21:47:34 +00003032 if (status == MagickFalse)
3033 continue;
cristy117ff172010-08-15 21:35:32 +00003034 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3035 (width/2L),image->columns+width,width,exception);
cristy8b49f382012-01-17 03:11:03 +00003036 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003037 exception);
cristy4c08aed2011-07-01 19:47:50 +00003038 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003039 {
3040 status=MagickFalse;
3041 continue;
3042 }
cristybb503372010-05-27 20:51:26 +00003043 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003044 {
cristybb503372010-05-27 20:51:26 +00003045 register ssize_t
cristy1e7aa312011-09-10 20:01:36 +00003046 i;
cristy3ed852e2009-09-05 21:47:34 +00003047
cristy1e7aa312011-09-10 20:01:36 +00003048 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3049 {
3050 MagickRealType
3051 alpha,
3052 gamma,
3053 intensity,
3054 pixel;
cristy117ff172010-08-15 21:35:32 +00003055
cristy1e7aa312011-09-10 20:01:36 +00003056 PixelChannel
3057 channel;
3058
3059 PixelTrait
3060 blur_traits,
3061 traits;
3062
3063 register const double
3064 *restrict k;
3065
3066 register const Quantum
3067 *restrict pixels;
3068
3069 register ssize_t
3070 u;
3071
3072 ssize_t
3073 v;
3074
cristye2a912b2011-12-05 20:02:07 +00003075 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003076 traits=GetPixelChannelMapTraits(image,channel);
cristy1e7aa312011-09-10 20:01:36 +00003077 blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3078 if ((traits == UndefinedPixelTrait) ||
3079 (blur_traits == UndefinedPixelTrait))
3080 continue;
cristyd09f8802012-02-04 16:44:10 +00003081 if (((blur_traits & CopyPixelTrait) != 0) ||
3082 (GetPixelMask(image,p) != 0))
cristy3ed852e2009-09-05 21:47:34 +00003083 {
cristy0beccfa2011-09-25 20:47:53 +00003084 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00003085 continue;
cristy3ed852e2009-09-05 21:47:34 +00003086 }
cristy1e7aa312011-09-10 20:01:36 +00003087 k=kernel;
3088 pixel=bias;
3089 pixels=p;
cristyc8523c12011-09-13 00:02:53 +00003090 intensity=(MagickRealType) GetPixelIntensity(image,p+center);
cristy1e7aa312011-09-10 20:01:36 +00003091 gamma=0.0;
3092 if ((blur_traits & BlendPixelTrait) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003093 {
cristy1e7aa312011-09-10 20:01:36 +00003094 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003095 {
cristy1e7aa312011-09-10 20:01:36 +00003096 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003097 {
cristy1e7aa312011-09-10 20:01:36 +00003098 contrast=GetPixelIntensity(image,pixels)-intensity;
3099 if (fabs(contrast) < threshold)
3100 {
3101 pixel+=(*k)*pixels[i];
3102 gamma+=(*k);
3103 }
3104 k++;
3105 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003106 }
cristy1e7aa312011-09-10 20:01:36 +00003107 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003108 }
cristy1e7aa312011-09-10 20:01:36 +00003109 if (fabs((double) gamma) < MagickEpsilon)
3110 {
cristy0beccfa2011-09-25 20:47:53 +00003111 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00003112 continue;
3113 }
3114 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00003115 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00003116 continue;
3117 }
3118 for (v=0; v < (ssize_t) width; v++)
3119 {
3120 for (u=0; u < (ssize_t) width; u++)
3121 {
3122 contrast=GetPixelIntensity(image,pixels)-intensity;
3123 if (fabs(contrast) < threshold)
3124 {
3125 alpha=(MagickRealType) (QuantumScale*
3126 GetPixelAlpha(image,pixels));
3127 pixel+=(*k)*alpha*pixels[i];
3128 gamma+=(*k)*alpha;
3129 }
3130 k++;
3131 pixels+=GetPixelChannels(image);
3132 }
3133 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003134 }
cristy1e7aa312011-09-10 20:01:36 +00003135 if (fabs((double) gamma) < MagickEpsilon)
3136 {
cristy0beccfa2011-09-25 20:47:53 +00003137 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00003138 continue;
3139 }
3140 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristy0beccfa2011-09-25 20:47:53 +00003141 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00003142 }
cristyed231572011-07-14 02:18:59 +00003143 p+=GetPixelChannels(image);
3144 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003145 }
3146 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3147 if (sync == MagickFalse)
3148 status=MagickFalse;
3149 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3150 {
3151 MagickBooleanType
3152 proceed;
3153
cristyb5d5f722009-11-04 03:03:49 +00003154#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003155 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003156#endif
3157 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3158 image->rows);
3159 if (proceed == MagickFalse)
3160 status=MagickFalse;
3161 }
3162 }
3163 blur_image->type=image->type;
3164 blur_view=DestroyCacheView(blur_view);
3165 image_view=DestroyCacheView(image_view);
cristy8b49f382012-01-17 03:11:03 +00003166 kernel=(double *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00003167 if (status == MagickFalse)
3168 blur_image=DestroyImage(blur_image);
3169 return(blur_image);
3170}
3171
3172/*
3173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174% %
3175% %
3176% %
3177% S h a d e I m a g e %
3178% %
3179% %
3180% %
3181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182%
3183% ShadeImage() shines a distant light on an image to create a
3184% three-dimensional effect. You control the positioning of the light with
3185% azimuth and elevation; azimuth is measured in degrees off the x axis
3186% and elevation is measured in pixels above the Z axis.
3187%
3188% The format of the ShadeImage method is:
3189%
3190% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3191% const double azimuth,const double elevation,ExceptionInfo *exception)
3192%
3193% A description of each parameter follows:
3194%
3195% o image: the image.
3196%
3197% o gray: A value other than zero shades the intensity of each pixel.
3198%
3199% o azimuth, elevation: Define the light source direction.
3200%
3201% o exception: return any errors or warnings in this structure.
3202%
3203*/
3204MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3205 const double azimuth,const double elevation,ExceptionInfo *exception)
3206{
3207#define ShadeImageTag "Shade/Image"
3208
cristyc4c8d132010-01-07 01:58:38 +00003209 CacheView
3210 *image_view,
3211 *shade_view;
3212
cristy3ed852e2009-09-05 21:47:34 +00003213 Image
3214 *shade_image;
3215
cristy3ed852e2009-09-05 21:47:34 +00003216 MagickBooleanType
3217 status;
3218
cristybb503372010-05-27 20:51:26 +00003219 MagickOffsetType
3220 progress;
3221
cristy3ed852e2009-09-05 21:47:34 +00003222 PrimaryInfo
3223 light;
3224
cristybb503372010-05-27 20:51:26 +00003225 ssize_t
3226 y;
3227
cristy3ed852e2009-09-05 21:47:34 +00003228 /*
3229 Initialize shaded image attributes.
3230 */
3231 assert(image != (const Image *) NULL);
3232 assert(image->signature == MagickSignature);
3233 if (image->debug != MagickFalse)
3234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3235 assert(exception != (ExceptionInfo *) NULL);
3236 assert(exception->signature == MagickSignature);
3237 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3238 if (shade_image == (Image *) NULL)
3239 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003240 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003241 {
cristy3ed852e2009-09-05 21:47:34 +00003242 shade_image=DestroyImage(shade_image);
3243 return((Image *) NULL);
3244 }
3245 /*
3246 Compute the light vector.
3247 */
3248 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3249 cos(DegreesToRadians(elevation));
3250 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3251 cos(DegreesToRadians(elevation));
3252 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3253 /*
3254 Shade image.
3255 */
3256 status=MagickTrue;
3257 progress=0;
3258 image_view=AcquireCacheView(image);
3259 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003260#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003261 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003262#endif
cristybb503372010-05-27 20:51:26 +00003263 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003264 {
3265 MagickRealType
3266 distance,
3267 normal_distance,
3268 shade;
3269
3270 PrimaryInfo
3271 normal;
3272
cristy4c08aed2011-07-01 19:47:50 +00003273 register const Quantum
cristy1e7aa312011-09-10 20:01:36 +00003274 *restrict center,
cristyc47d1f82009-11-26 01:44:43 +00003275 *restrict p,
cristy1e7aa312011-09-10 20:01:36 +00003276 *restrict post,
3277 *restrict pre;
cristy3ed852e2009-09-05 21:47:34 +00003278
cristy4c08aed2011-07-01 19:47:50 +00003279 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003280 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003281
cristy117ff172010-08-15 21:35:32 +00003282 register ssize_t
3283 x;
3284
cristy3ed852e2009-09-05 21:47:34 +00003285 if (status == MagickFalse)
3286 continue;
3287 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3288 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3289 exception);
cristy4c08aed2011-07-01 19:47:50 +00003290 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003291 {
3292 status=MagickFalse;
3293 continue;
3294 }
3295 /*
3296 Shade this row of pixels.
3297 */
3298 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristy1e7aa312011-09-10 20:01:36 +00003299 pre=p+GetPixelChannels(image);
3300 center=pre+(image->columns+2)*GetPixelChannels(image);
3301 post=center+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003302 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003303 {
cristy1e7aa312011-09-10 20:01:36 +00003304 register ssize_t
3305 i;
3306
cristy3ed852e2009-09-05 21:47:34 +00003307 /*
3308 Determine the surface normal and compute shading.
3309 */
cristy1e7aa312011-09-10 20:01:36 +00003310 normal.x=(double) (GetPixelIntensity(image,pre-GetPixelChannels(image))+
3311 GetPixelIntensity(image,center-GetPixelChannels(image))+
3312 GetPixelIntensity(image,post-GetPixelChannels(image))-
3313 GetPixelIntensity(image,pre+GetPixelChannels(image))-
3314 GetPixelIntensity(image,center+GetPixelChannels(image))-
3315 GetPixelIntensity(image,post+GetPixelChannels(image)));
3316 normal.y=(double) (GetPixelIntensity(image,post-GetPixelChannels(image))+
3317 GetPixelIntensity(image,post)+GetPixelIntensity(image,post+
3318 GetPixelChannels(image))-GetPixelIntensity(image,pre-
3319 GetPixelChannels(image))-GetPixelIntensity(image,pre)-
3320 GetPixelIntensity(image,pre+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003321 if ((normal.x == 0.0) && (normal.y == 0.0))
3322 shade=light.z;
3323 else
3324 {
3325 shade=0.0;
3326 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3327 if (distance > MagickEpsilon)
3328 {
3329 normal_distance=
3330 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3331 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3332 shade=distance/sqrt((double) normal_distance);
3333 }
3334 }
cristy1e7aa312011-09-10 20:01:36 +00003335 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3336 {
3337 PixelChannel
3338 channel;
3339
3340 PixelTrait
3341 shade_traits,
3342 traits;
3343
cristye2a912b2011-12-05 20:02:07 +00003344 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003345 traits=GetPixelChannelMapTraits(image,channel);
cristy1e7aa312011-09-10 20:01:36 +00003346 shade_traits=GetPixelChannelMapTraits(shade_image,channel);
3347 if ((traits == UndefinedPixelTrait) ||
3348 (shade_traits == UndefinedPixelTrait))
3349 continue;
cristyd09f8802012-02-04 16:44:10 +00003350 if (((shade_traits & CopyPixelTrait) != 0) ||
3351 (GetPixelMask(image,pre) != 0))
cristy1e7aa312011-09-10 20:01:36 +00003352 {
cristy0beccfa2011-09-25 20:47:53 +00003353 SetPixelChannel(shade_image,channel,center[i],q);
cristy1e7aa312011-09-10 20:01:36 +00003354 continue;
3355 }
3356 if (gray != MagickFalse)
3357 {
cristy0beccfa2011-09-25 20:47:53 +00003358 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
cristy1e7aa312011-09-10 20:01:36 +00003359 continue;
3360 }
cristy0beccfa2011-09-25 20:47:53 +00003361 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3362 center[i]),q);
cristy1e7aa312011-09-10 20:01:36 +00003363 }
3364 pre+=GetPixelChannels(image);
3365 center+=GetPixelChannels(image);
3366 post+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00003367 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003368 }
3369 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3370 status=MagickFalse;
3371 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3372 {
3373 MagickBooleanType
3374 proceed;
3375
cristyb5d5f722009-11-04 03:03:49 +00003376#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003377 #pragma omp critical (MagickCore_ShadeImage)
cristy3ed852e2009-09-05 21:47:34 +00003378#endif
3379 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3380 if (proceed == MagickFalse)
3381 status=MagickFalse;
3382 }
3383 }
3384 shade_view=DestroyCacheView(shade_view);
3385 image_view=DestroyCacheView(image_view);
3386 if (status == MagickFalse)
3387 shade_image=DestroyImage(shade_image);
3388 return(shade_image);
3389}
3390
3391/*
3392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3393% %
3394% %
3395% %
3396% S h a r p e n I m a g e %
3397% %
3398% %
3399% %
3400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3401%
3402% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3403% operator of the given radius and standard deviation (sigma). For
3404% reasonable results, radius should be larger than sigma. Use a radius of 0
3405% and SharpenImage() selects a suitable radius for you.
3406%
3407% Using a separable kernel would be faster, but the negative weights cancel
3408% out on the corners of the kernel producing often undesirable ringing in the
3409% filtered result; this can be avoided by using a 2D gaussian shaped image
3410% sharpening kernel instead.
3411%
3412% The format of the SharpenImage method is:
3413%
3414% Image *SharpenImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +00003415% const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003416%
3417% A description of each parameter follows:
3418%
3419% o image: the image.
3420%
cristy3ed852e2009-09-05 21:47:34 +00003421% o radius: the radius of the Gaussian, in pixels, not counting the center
3422% pixel.
3423%
3424% o sigma: the standard deviation of the Laplacian, in pixels.
3425%
cristy05c0c9a2011-09-05 23:16:13 +00003426% o bias: bias.
3427%
cristy3ed852e2009-09-05 21:47:34 +00003428% o exception: return any errors or warnings in this structure.
3429%
3430*/
cristy3ed852e2009-09-05 21:47:34 +00003431MagickExport Image *SharpenImage(const Image *image,const double radius,
cristy05c0c9a2011-09-05 23:16:13 +00003432 const double sigma,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003433{
cristy3ed852e2009-09-05 21:47:34 +00003434 double
cristy47e00502009-12-17 19:19:57 +00003435 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003436
3437 Image
3438 *sharp_image;
3439
cristy41cbe682011-07-15 19:12:37 +00003440 KernelInfo
3441 *kernel_info;
3442
cristybb503372010-05-27 20:51:26 +00003443 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003444 i;
3445
cristybb503372010-05-27 20:51:26 +00003446 size_t
cristy3ed852e2009-09-05 21:47:34 +00003447 width;
3448
cristy117ff172010-08-15 21:35:32 +00003449 ssize_t
3450 j,
3451 u,
3452 v;
3453
cristy3ed852e2009-09-05 21:47:34 +00003454 assert(image != (const Image *) NULL);
3455 assert(image->signature == MagickSignature);
3456 if (image->debug != MagickFalse)
3457 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3458 assert(exception != (ExceptionInfo *) NULL);
3459 assert(exception->signature == MagickSignature);
3460 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003461 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003462 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003463 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003464 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3465 kernel_info->width=width;
3466 kernel_info->height=width;
anthony736a1602011-10-06 12:38:17 +00003467 kernel_info->bias=bias; /* FUTURE: user bias - non-sensical! */
cristy41cbe682011-07-15 19:12:37 +00003468 kernel_info->signature=MagickSignature;
cristya96f2492011-12-14 18:25:41 +00003469 kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
3470 kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
3471 if (kernel_info->values == (MagickRealType *) NULL)
cristy41cbe682011-07-15 19:12:37 +00003472 {
3473 kernel_info=DestroyKernelInfo(kernel_info);
3474 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3475 }
cristy3ed852e2009-09-05 21:47:34 +00003476 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003477 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003478 i=0;
3479 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003480 {
cristy47e00502009-12-17 19:19:57 +00003481 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003482 {
cristy41cbe682011-07-15 19:12:37 +00003483 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3484 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3485 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003486 i++;
3487 }
3488 }
cristy41cbe682011-07-15 19:12:37 +00003489 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy5e6be1e2011-07-16 01:23:39 +00003490 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003491 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003492 return(sharp_image);
3493}
3494
3495/*
3496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3497% %
3498% %
3499% %
3500% S p r e a d I m a g e %
3501% %
3502% %
3503% %
3504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3505%
3506% SpreadImage() is a special effects method that randomly displaces each
3507% pixel in a block defined by the radius parameter.
3508%
3509% The format of the SpreadImage method is:
3510%
3511% Image *SpreadImage(const Image *image,const double radius,
cristy5c4e2582011-09-11 19:21:03 +00003512% const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003513%
3514% A description of each parameter follows:
3515%
3516% o image: the image.
3517%
cristy5c4e2582011-09-11 19:21:03 +00003518% o radius: choose a random pixel in a neighborhood of this extent.
3519%
3520% o method: the pixel interpolation method.
cristy3ed852e2009-09-05 21:47:34 +00003521%
3522% o exception: return any errors or warnings in this structure.
3523%
3524*/
3525MagickExport Image *SpreadImage(const Image *image,const double radius,
cristy5c4e2582011-09-11 19:21:03 +00003526 const PixelInterpolateMethod method,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003527{
3528#define SpreadImageTag "Spread/Image"
3529
cristyfa112112010-01-04 17:48:07 +00003530 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003531 *image_view,
3532 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003533
cristy3ed852e2009-09-05 21:47:34 +00003534 Image
3535 *spread_image;
3536
cristy3ed852e2009-09-05 21:47:34 +00003537 MagickBooleanType
3538 status;
3539
cristybb503372010-05-27 20:51:26 +00003540 MagickOffsetType
3541 progress;
3542
cristy3ed852e2009-09-05 21:47:34 +00003543 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003544 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003545
cristybb503372010-05-27 20:51:26 +00003546 size_t
cristy3ed852e2009-09-05 21:47:34 +00003547 width;
3548
cristybb503372010-05-27 20:51:26 +00003549 ssize_t
3550 y;
3551
cristy3ed852e2009-09-05 21:47:34 +00003552 /*
3553 Initialize spread image attributes.
3554 */
3555 assert(image != (Image *) NULL);
3556 assert(image->signature == MagickSignature);
3557 if (image->debug != MagickFalse)
3558 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3559 assert(exception != (ExceptionInfo *) NULL);
3560 assert(exception->signature == MagickSignature);
3561 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3562 exception);
3563 if (spread_image == (Image *) NULL)
3564 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00003565 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003566 {
cristy3ed852e2009-09-05 21:47:34 +00003567 spread_image=DestroyImage(spread_image);
3568 return((Image *) NULL);
3569 }
3570 /*
3571 Spread image.
3572 */
3573 status=MagickTrue;
3574 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003575 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003576 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003577 image_view=AcquireCacheView(image);
3578 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003579#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00003580 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003581#endif
cristybe82ad52011-09-06 01:17:20 +00003582 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003583 {
cristy5c9e6f22010-09-17 17:31:01 +00003584 const int
3585 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003586
cristybe82ad52011-09-06 01:17:20 +00003587 register const Quantum
3588 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003589
cristy4c08aed2011-07-01 19:47:50 +00003590 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003591 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003592
cristy117ff172010-08-15 21:35:32 +00003593 register ssize_t
3594 x;
3595
cristy3ed852e2009-09-05 21:47:34 +00003596 if (status == MagickFalse)
3597 continue;
cristybe82ad52011-09-06 01:17:20 +00003598 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy9f7e7cb2011-03-26 00:49:57 +00003599 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003600 exception);
cristybe82ad52011-09-06 01:17:20 +00003601 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003602 {
3603 status=MagickFalse;
3604 continue;
3605 }
cristybe82ad52011-09-06 01:17:20 +00003606 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003607 {
cristybe82ad52011-09-06 01:17:20 +00003608 PointInfo
3609 point;
3610
cristybe82ad52011-09-06 01:17:20 +00003611 point.x=GetPseudoRandomValue(random_info[id]);
3612 point.y=GetPseudoRandomValue(random_info[id]);
cristy5c4e2582011-09-11 19:21:03 +00003613 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3614 (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
cristyed231572011-07-14 02:18:59 +00003615 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003616 }
cristy9f7e7cb2011-03-26 00:49:57 +00003617 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003618 status=MagickFalse;
3619 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3620 {
3621 MagickBooleanType
3622 proceed;
3623
cristyb557a152011-02-22 12:14:30 +00003624#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003625 #pragma omp critical (MagickCore_SpreadImage)
cristy3ed852e2009-09-05 21:47:34 +00003626#endif
3627 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3628 if (proceed == MagickFalse)
3629 status=MagickFalse;
3630 }
3631 }
cristy9f7e7cb2011-03-26 00:49:57 +00003632 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003633 image_view=DestroyCacheView(image_view);
3634 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003635 return(spread_image);
3636}
3637
3638/*
3639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3640% %
3641% %
3642% %
3643% U n s h a r p M a s k I m a g e %
3644% %
3645% %
3646% %
3647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3648%
3649% UnsharpMaskImage() sharpens one or more image channels. We convolve the
3650% image with a Gaussian operator of the given radius and standard deviation
3651% (sigma). For reasonable results, radius should be larger than sigma. Use a
3652% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3653%
3654% The format of the UnsharpMaskImage method is:
3655%
3656% Image *UnsharpMaskImage(const Image *image,const double radius,
3657% const double sigma,const double amount,const double threshold,
3658% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003659%
3660% A description of each parameter follows:
3661%
3662% o image: the image.
3663%
cristy3ed852e2009-09-05 21:47:34 +00003664% o radius: the radius of the Gaussian, in pixels, not counting the center
3665% pixel.
3666%
3667% o sigma: the standard deviation of the Gaussian, in pixels.
3668%
3669% o amount: the percentage of the difference between the original and the
3670% blur image that is added back into the original.
3671%
3672% o threshold: the threshold in pixels needed to apply the diffence amount.
3673%
3674% o exception: return any errors or warnings in this structure.
3675%
3676*/
cristy31bb6272011-11-20 23:57:25 +00003677MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3678 const double sigma,const double amount,const double threshold,
3679 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003680{
3681#define SharpenImageTag "Sharpen/Image"
3682
cristyc4c8d132010-01-07 01:58:38 +00003683 CacheView
3684 *image_view,
3685 *unsharp_view;
3686
cristy3ed852e2009-09-05 21:47:34 +00003687 Image
3688 *unsharp_image;
3689
cristy3ed852e2009-09-05 21:47:34 +00003690 MagickBooleanType
3691 status;
3692
cristybb503372010-05-27 20:51:26 +00003693 MagickOffsetType
3694 progress;
3695
cristy3ed852e2009-09-05 21:47:34 +00003696 MagickRealType
3697 quantum_threshold;
3698
cristybb503372010-05-27 20:51:26 +00003699 ssize_t
3700 y;
3701
cristy3ed852e2009-09-05 21:47:34 +00003702 assert(image != (const Image *) NULL);
3703 assert(image->signature == MagickSignature);
3704 if (image->debug != MagickFalse)
3705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3706 assert(exception != (ExceptionInfo *) NULL);
cristy05c0c9a2011-09-05 23:16:13 +00003707 unsharp_image=BlurImage(image,radius,sigma,image->bias,exception);
cristy3ed852e2009-09-05 21:47:34 +00003708 if (unsharp_image == (Image *) NULL)
3709 return((Image *) NULL);
3710 quantum_threshold=(MagickRealType) QuantumRange*threshold;
3711 /*
3712 Unsharp-mask image.
3713 */
3714 status=MagickTrue;
3715 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003716 image_view=AcquireCacheView(image);
3717 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00003718#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00003719 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003720#endif
cristybb503372010-05-27 20:51:26 +00003721 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003722 {
cristy4c08aed2011-07-01 19:47:50 +00003723 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003724 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003725
cristy4c08aed2011-07-01 19:47:50 +00003726 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003727 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003728
cristy117ff172010-08-15 21:35:32 +00003729 register ssize_t
3730 x;
3731
cristy3ed852e2009-09-05 21:47:34 +00003732 if (status == MagickFalse)
3733 continue;
3734 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy8b49f382012-01-17 03:11:03 +00003735 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003736 exception);
cristy4c08aed2011-07-01 19:47:50 +00003737 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003738 {
3739 status=MagickFalse;
3740 continue;
3741 }
cristybb503372010-05-27 20:51:26 +00003742 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003743 {
cristy7f3a0d12011-09-05 23:27:59 +00003744 register ssize_t
3745 i;
3746
3747 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3748 {
3749 MagickRealType
3750 pixel;
3751
3752 PixelChannel
3753 channel;
3754
3755 PixelTrait
3756 traits,
3757 unsharp_traits;
3758
cristye2a912b2011-12-05 20:02:07 +00003759 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +00003760 traits=GetPixelChannelMapTraits(image,channel);
cristy7f3a0d12011-09-05 23:27:59 +00003761 unsharp_traits=GetPixelChannelMapTraits(unsharp_image,channel);
3762 if ((traits == UndefinedPixelTrait) ||
3763 (unsharp_traits == UndefinedPixelTrait))
3764 continue;
cristyd09f8802012-02-04 16:44:10 +00003765 if (((unsharp_traits & CopyPixelTrait) != 0) ||
3766 (GetPixelMask(image,p) != 0))
cristy7f3a0d12011-09-05 23:27:59 +00003767 {
cristy0beccfa2011-09-25 20:47:53 +00003768 SetPixelChannel(unsharp_image,channel,p[i],q);
cristy7f3a0d12011-09-05 23:27:59 +00003769 continue;
3770 }
cristy0beccfa2011-09-25 20:47:53 +00003771 pixel=p[i]-(MagickRealType) GetPixelChannel(unsharp_image,channel,q);
cristy7f3a0d12011-09-05 23:27:59 +00003772 if (fabs(2.0*pixel) < quantum_threshold)
3773 pixel=(MagickRealType) p[i];
3774 else
3775 pixel=(MagickRealType) p[i]+amount*pixel;
cristy0beccfa2011-09-25 20:47:53 +00003776 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
cristy7f3a0d12011-09-05 23:27:59 +00003777 }
cristyed231572011-07-14 02:18:59 +00003778 p+=GetPixelChannels(image);
3779 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00003780 }
3781 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
3782 status=MagickFalse;
3783 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3784 {
3785 MagickBooleanType
3786 proceed;
3787
cristyb5d5f722009-11-04 03:03:49 +00003788#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003789 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00003790#endif
3791 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
3792 if (proceed == MagickFalse)
3793 status=MagickFalse;
3794 }
3795 }
3796 unsharp_image->type=image->type;
3797 unsharp_view=DestroyCacheView(unsharp_view);
3798 image_view=DestroyCacheView(image_view);
3799 if (status == MagickFalse)
3800 unsharp_image=DestroyImage(unsharp_image);
3801 return(unsharp_image);
3802}